Category: Common Knowledge

  • Cách tiếp cận mới trong tổ chức GIT repository dự án

    Cách tiếp cận mới trong tổ chức GIT repository dự án

    Chắc hẳn phần lớn chúng ta ở đây (suy đoán của mình) chưa từng tham gia đóng góp (contribute) vào một opensource nào đó ở trên Github, kể cả bản thân mình cho đến gần đây. Contribute mình muốn nói đến ở đây không phải về việc chúng ta tự publish các opensource library lên chính repository của chúng ta trên Github, cũng không phải việc trả lời các issues trên các Github repository, và cũng không phải thực hiện review các PR (Pull Request) trên các Github repository. Cái mà mình muốn nói ở đây là công việc chúng ta tạo ra các dòng code cho các feature mới, để fixbug, để refactor,… và rồi đưa những dòng code đó vào trong các opensource trên Github.

    Phần lớn chúng ta đang sử dụng trực tiếp các opensource từ các repository center, hoặc khi gặp bug chúng ta clone về và tự fix bug trong đó, và dùng nó như là một submodule ở trong project (Việc này cần chú ý đến License của opensource, và mục đích project của chúng ta đang làm). Có khá nhiều nhược điểm khi làm bằng cách vừa rồi, nhưng để đảm bảo đúng mục đích của bài này mình xin phép chưa nói đến ở đây, chúng ta có thể thảo luận ở phần comment. Tuy việc contribute cho opensource không phải là mục đích chính của mình trong bài viết này, nhưng hi vọng qua đây bạn sẽ nắm được các bước cơ bản để contribute cho một opensource nào đó trên Github.

    Mục đích chính của mình trong bài này là việc chúng ta cùng cân nhắc sử dụng cách tiếp cận contribute để áp dụng cho các dự án trong của công ty, cùng nhìn các khía cạnh, ưu nhược điểm giữa 2 cách tiếp cận đó là truyền thống, và contribute.

    Cách tiếp cận truyền thống và vấn đề gặp phải

    Đầu tiên mình sẽ summarize về cách tiếp cận truyền thống mà chúng ta đang làm trong một project.

    Chúng ta có một repository trên Gitlab, thêm các thành viên của dự án vào trong dự án theo từng rule, setup một số rule cơ bản về merge/rebase, branches, template,… Sau đó trong giai đoạn phát triển, mỗi thành viên sẽ tạo branch (theo format của team) để thực hiện code trên đó, code, commit, code, commit,… tạo MR/PR.

    Vấn đề gặp phải

    Những step trên đâu đấy là các step cơ bản để đưa các dự án về đích, còn về đích theo kiểu tan tác, hay hùng tráng thì đấy là một câu chuyện khác mà có thể mình sẽ chia sẻ ở một bài viết khác.

    Tất nhiên nếu mọi chuyện đều đẹp đẽ thì chúng ta đã không có bài này để thảo luận. Dưới đây là một số những điều mình nhận thấy sau nhiều tháng, năm nhìn lại project:

    • Git Tree là một cái gì đấy cực kì kinh khủng và không muốn nhìn vào nó.
    • Có hàng chục, hàng trăm branches, già có, trẻ có với muôn hình muôn vẻ.

    Với Git Tree thì đó là một câu chuyện rất hay, nhưng xin phép để lại cho một bài khác đầy đủ hơn để giải quyết nó. Ở đây mình muốn nói đến về branches. Sự hỗn loạn, nhập nhằng của branches trong project là sự đóng góp, cống hiến của mỗi thành viên trong dự án. Ở đây mình bỏ qua các yếu tố như quên không xóa branch sau khi đã được merge vào branch chính, hay branch sai format, branch từa lưa tạo linh tinh xong không xóa, thì mỗi một thành viên trong dự án, vẫn có những branch cá nhân cần thiết như để thử nghiệm, một số branch đang trong quá trình phát triển, fix bug này kia. Với mỗi thành viên đã có số branch cá nhân như vậy, thì liệu một team nhiều người thì lượng branch tại một thời điểm sẽ như nào? Sẽ rất rất lớn phải không?

    Cách tiếp cận mới – Contribute

    Idea của cách tiếp cận này để giải quyết vấn đề về branches đó là:

    Mỗi thành viên trong dự án sẽ không làm việc trực tiếp với repository chính của dự án nữa, mà sẽ làm việc với repository riêng của chính mình (mỗi thành viên), sau đó contribute vào repository chính của dự án.

    Với cách tiếp cận này, tất cả những gì bạn làm, bạn nghịch, bạn vọc trên sourcecode nó thuộc về bạn, và chính bạn mà thôi, không ai biết hay nhìn tới cả (trừ khi người đó muốn). Có nghĩa là tất cả những thứ hổ lốn mà có thể bạn tạo ra sẽ không làm ảnh hưởng đến người khác. Cuối cùng là những cái gì là tinh hoa nhất của bạn, thì bạn contribute (tạo MR/PR) vào trong repository chính của dự án, đến lúc đó ai ai cũng trầm trồ ngưỡng mộ những gì bạn đã đóng góp.

    Lý thuyết thì là vậy, tiếp theo đây, chúng ta sẽ phải thực hiện hóa nó. Các step làm sao để có thể thực hiện contribute được thì các bạn tham khảo và đọc tại đây.

    Mô hình tổng quát có thể biểu diễn qua sơ đồ dưới đây (Mình assume các bạn đã hiểu hoặc đã đọc bài hướng dẫn contribute ở trên):

    Như bạn thấy, tất cả những branches, những đoạn code thử nghiệm, những gì chưa hoàn hảo, bạn gói gọn nó trong repository của bạn mà không làm ảnh hưởng đến toàn bộ dự án. Và chúng ta cũng chỉ quan tâm tới workspace mà mình đang làm việc mà thôi, rất tập trung, và không bị làm phiền bởi người khác.

    Một vài câu hỏi thường được hỏi khi sử dụng cách tiếp cận mới này

    (Mình assume các bạn đã hiểu hoặc đã đọc bài hướng dẫn contribute ở trên)

    1. Nếu source code nằm ở 2 repository khác biệt nhau như vậy, làm sao để source code ở fork repository của mình là mới nhất?

    Trả lời: Ở máy tính của bạn đã được setup 2 remote URL, 1 là fork repository của bạn (origin), 2 là repository nguồn (upstream). Bạn có thể fetch source code ở upstream sau đó rebase sang source code local của bạn, sau đó push lên origin của bạn. Thật ra chúng ta sẽ thường quan tâm nhất trạng thái source code ở branch mà chúng ta đang dùng làm source base đó là branch develop, hoặc master.

    2. Khi làm việc trong dự án, thỉnh thoảng vẫn cần base trên source code của đồng nghiệp để làm, vậy thì phải làm thế nào?

    Trả lời: Tương tự với cách mà chúng ta setup git remote upstream, chúng ta hoàn toàn có thể setup git remote vào fork repository của đồng nghiệp bằng cách git remote add TEN_DONG_NGHIEP FORK_REPOSITOY_URL_DONG_NGHIEP. Ví dụ như git remote add DoanNH3 https://github.com/ngohado/project-a. Sau đó chúng ta hoàn toàn có thể base trên branch nào đó của đồng nghiệp để thực hiện tiếp.

    Sau khi làm việc xong, chúng ta có thể remove remote của đồng nghiệp đi để tránh rối rắm workspace của chúng ta.

    3. Nếu tôi là teamlead của dự án, tôi muốn xem công việc hiện tại của các thành viên xem tình hình code đến đâu rồi, code thế nào, chẳng lẽ tôi phải remote đến tất cả các repository của member à? Liệu có cách nào tốt hơn không?

    Trả lời: Bản thân nếu bạn phải remote sang tất cả repository của member, cũng sẽ không khác gì, chứ không tồi tệ hơn cách cũ. Tuy nhiên nếu là mình, mình sẽ thực theo cách như sau:

    • Member sẽ tạo MR/PR ngay khi bạn ấy push commit lần đầu tiên (mình prefer anh em member luôn luôn push ít nhất 1 lần / ngày làm việc, đó là trước khi rời office).
    • Mình thích review theo từng commit chứ không phải tổng thể MR/PR (tất nhiên cái này cả team phải chung một mindset thì mới làm được), nên nếu commit nào đã hoàn thiện bạn ấy đẩy lên mình có thể review được ngay, còn những commit vẫn in-progress chưa sẵn sàng thì có thể thêm prefix vào commit message để báo với teamlead chưa review commit đó (Bước refine commit sau khi hoàn tất mình sẽ có một bài chia sẻ riêng).

    Với cách trên thì mình có thể view được tình hình của team qua các MR/PR, qua các commit đã hoàn thiện, và các commit vẫn đang trong progress.

    Kết luận

    Bản thân mình chưa trải nghiệm cách tiếp cận này trên một project dài hơi, hay lớn. Mình tin là vẫn sẽ có những question cho cách tiếp cận này, nếu bạn có hãy raise lên ở dưới phần comment để chúng ta cùng nhau nghĩ cách giải quyết. Nếu sắp tới khi mình tham gia vào một dự án nào mới, mình nhất định sẽ đề xuất cách tiếp cận này để trải nghiệm, có đánh giá tốt hơn và rồi sẽ chia sẻ lại cho mọi người. Thật ra thì không hẳn là dự án mới mới có thể áp dụng được, ngay những dự án đang chạy hoàn toàn có thể chuyển sang cách này, nếu thấy hợp lý thì hãy thử đề xuất với teamlead, hay với team của chính các bạn nhé. Cảm ơn các bạn đã đọc đến đây, rất mong nhận được sử phản hồi từ các bạn.

  • How to build an end to end scalable visual search system with AI Computer Vision and AWS

    How to build an end to end scalable visual search system with AI Computer Vision and AWS

    With the rise of e-commerce, online retail, visual search is a rapid trend because they are largely driven by visual content. In this article, I will share with you guys the visual search project that was built in 2018 for my Japanese partner.

    What are the problems?

    My partner is one of the biggest retailers of toys, clothing, and baby product in Japan. They have lots of stores all over the world. On holiday, the long queues of customers wait to checkout happen in lots of stores. So to solve this problem, they decide to apply the new store like Amazon Go, no queues, no checkout, just walk out of the store. In short, this not only improves their customers’ buying experience but also maximizes revenue growth.

    Why do we need Visual Search?

    provider

    My partner has lots of providers. Each of them distributes different types of products. The products will be updated frequently by weekly, monthly, or yearly. So visual search system needs to be updated the same as the scale of products.

    What is the core technology here?

    provider

    By the time I started this project in 2018, Triplet loss had proved efficient in Face Recognition. With that starting point, we decide to apply Triplet Loss as our core visual search technology because our problem is quite similar to Face Recognition. To get the highest accuracy and make the system scalable, we classify products into different categories and subcategories because Triplet loss will have the best performance when all the products are in the same domain property. For example, when you do Face Recognition, all the images are facial, right? Then in our problem, we train all the toys that have a similar domain with one AI Model. For example, the Lego toys will be trained together, the Figures will be trained together, and so on. And not only that, one of the most advantages here is when new products need to be updated, our system don’t need to re-training the whole model, the whole process which can reduce the cost. Normally, it’s very costly and takes us lots of time for training an AI model.

    The architecture overview

    provider As you can see, we have two main components: Serving and Training. The training component is built to fit with Admin, Operator, Providers, and Developers with different purposes. For any Machine Learning production, one of the most challenging parts is how your system can run automatically with the minimum of human interference. The serving component will host all APIs needed for our web app and mobile app.

    Triplet Loss training strategy

    Our strategy here to get the best model is: Clustering the categories to group categories with similar type samples. For each anchor image, we will pick 1 hardest positive, 1 hardest negative among the image batch. We will keep multiple anchors and compute the centroid of anchors for each category. To reduce computation cost we will first match with anchor centroids. If the distance is lower than TL and higher than TH, we will give an immediate decision that the product exists and doesn’t exist respectively. We are using two thresholds. Lower threshold, TL, and higher threshold And in the end, our model has very good accuracy, for the trained data, the accuracy is over 99 percent and 97 percent for un-trained data respectively.

    Alt text

    Demo

    A Video Worth a Million words, so, please see this demo below on how we increase customer engagement with AR and AI.

    Alt text

    Conclusion

    This article is focused on sharing the overview flow, architecture, and an example use case when AI is applied in the real world. If you are curious more about the technology, the development, or the business side, please follow my next articles.

  • Software Architecture: Bắt đầu từ đâu? – Part 3 Soft Skills  – Continuous Delivery

    Software Architecture: Bắt đầu từ đâu? – Part 3 Soft Skills – Continuous Delivery

    Trong phần trước, mình đã nói về Soft Skill — Decision, trong phần này sẽ đề cập tới một loại khác mà sẽ gây nhiều tranh cãi: Continuous Delivery.

    Continuous Delivery, hãy nói về mặt khả năng, chính xác hơn là điểm khác biệt với Manual Delivery ở 3 yếu tố: an toàn, nhanh chóng và bền vững (hoặc dùng từ Việt một chút là ổn định).

    Trong bài viết này sẽ không đánh đồng Continuous Delivery với các kỹ thuật automate, mà mọi người hay khá nhầm lẫn với việc Automation Delivery (một trong những kỹ thuật của CD). Dù sao thì, mình chỉ muốn xem xét về mặt văn hóa trong dự án phần mềm.

    Tại sao cần Continuous Delivery?

    Thời gian — tất nhiên rồi, ai cũng muốn đưa những tính năng mới nhất cho người dùng một cách nhanh nhất, đưa hệ thống trở lại khi downtime nhanh nhất (Mean Time To Repair) đồng nghĩa với việc business được duy trì, tiền kiếm được không bị ngừng trệ.

    Hãy xem xét 2 loại thời gian:

    • Lead time: lượng thời gian từ khi bắt đầu và kết thúc của một công việc trong quy trình
    • Cycle time: khoảng thời gian mà giữa 2 công việc liên tiếp được kết thúc, hay còn có thể hiểu là Deployment Frequency cho dễ hiểu

    Continuous Delivery Pipeline sẽ được hình dung như này

    image

    Còn đây là Lead time image

    Đây là Cycle time nè image

    Vậy thời gian bao nhiêu là đủ?

    image

    Credit: Forsgren PhD, Nicole. Accelerate: The Science of Lean Software and DevOps: Building and Scaling High Performing Technology Organizations (Kindle Location 564). IT Revolution Press. Kindle Edition.

    Nhìn chung thì khi chúng ta cố gắng tăng tần suất deploy chúng ta sẽ có thể giảm MTTR, có nghĩa chúng ta sẽ không tạo ra một sản phẩm có tính bền vững cao (robustness — như cách tiếp cận truyền thống), khi đó khả năng tự phục hồi sau lỗi (resilience) của sản phẩm được tăng lên (một cách tự nhiên), cùng với đó là giảm khả năng xảy ra lỗi vì gần như khi tăng tần suất deploy đến hằng ngày hoặc nhiều lần một ngày sản phẩm sẽ gần như lúc nào cũng sẵn sàng để đưa lên môi trường production (production ready).

    Như vậy chúng ta sẽ có một mô hình:

    1. Continuous Integration:

      • Integration sớm và thường xuyên
      • Mọi người cần đồng bộ trunk (ý nói đến phiên bản được deploy) hằng ngày
    2. Continuous Deployment:

      • Điều này có để dễ dàng đạt được, khi source code ở trunk khá ổn định và Deploy chỉ là stage cuối cùng của Continuous Integration, như vậy việc phát hiện các lỗi và fix chúng cũng nhanh hơn, kể cả khi bạn có những thành viên không quá giỏi trong tay. Với các công cụ khác nhau như Sonarquebe hay chính Unit Test sẽ hỗ trợ developer tạo nên những sản phẩm tốt hơn.
    3. Continuous Delivery:

      • Sau 2 vấn đề bên trên source code ổn định, deploy thường xuyên, bản vá được xử lý nhanh chóng ít ảnh hưởng, cuối chùng chúng ta có một phần mềm luôn ở trạng thái sẵn sàng.

    Nhưng

    Có thể các bạn sẽ nghĩ hay có câu hỏi như này: Lỗi vẫn xảy ra thì sao?

    Ồ thì tất nhiên lỗi vẫn xảy ra, nhưng chúng ta sẽ có lỗi ở những thời điểm sớm hơn. Hầu hết mọi người sẽ gặp các vấn đề thường xảy ra khi chúng ta tích hợp (các thành phần trong phần mềm với nhau), điều này khá khó chịu, và mọi người sẽ có xu hướng trì hoãn. Tuy nhiên hãy nghĩ đến trường hợp này: trong ví dụ ở phần trước giả sử client cứ làm theo requirement hoặc UX/UI được cung cấp, backend thiết kế hoàn toàn khác tuy nhiên vẫn đầy đủ thông tin, client sẽ phải call nhiều API cho màn hình thay vì 1 API như trong suy nghĩ, điều đó dẫn đến việc một trong 2 cần thay đổi, việc này tốn khá nhiều công và tiềm ẩn rủi ro. Vậy nên khi tích hợp sớm chúng ta sẽ làm việc trơn tru trong quãng thời gian còn lại và sẵn sàng release thay vì dành vài tuần hoặc vài ngày cuối để thay đổi toàn bộ interface.

    Bring the pain forward

    Hay đầy đủ là if it hurts, do it more often, and bring the pain forward.

    image

    Quay lại vấn đề giữa mobile và backend, giả sử sẽ vẫn có những khác biệt về suy nghĩ xây dựng interface giữa 2 bên, tuy nhiên chúng ta đã làm nó sớm, và chia nhỏ nó ra và bằng cách backend thường xuyên release phiên bản API mới (chưa nói đến việc backward compatibility nhé), mobile sẽ tích hợp tốt hơn, nhanh hơn vì những thay đổi khá nhỏ và dễ dàng đáp ứng. Trong khi đó mobile team cũng build ra app thường xuyên hơn, tester có thể verify nhanh hơn, sản phẩm được ổn định hơn, ít tiềm ẩn lỗi hơn.

    Điều này khá giống trong lý thuyết phát triển phần mềm, chi phí sửa lỗi càng nhiều cho các lỗi ở giai đoạn muộn của dự án.

    Continuous Delivery bằng cách nào

    image

    Continuous Delivery cho chúng ta một cách tiếp cận khác về việc quản lý dự án, chúng ta có thể định nghĩa lại done không phải là code xong mà là được delivery thành công. Toàn bộ quá trình nên sử dụng đối đa các nền tảng tự động như Unit-test, Automation Test, điều đó làm tăng tốc độ feedback trên các bản vá của source code cũng như infrastructure. Điều này càng quan trọng hơn khi chi phí End-to-end testing càng ngày càng lớn, chúng nên được thay thế bằng các danh mục testing như dưới đây (multi-faceted testing portfolio), điều này càng quan trọng hơn trong thế giới hiện nay khi Devops là xu hướng, và các quyết định về release càng ngày càng gắn liên với tình huống về mặt nghiệp vụ thay vì sự quyết định của một đội quản lý về vận hành.

    Practice Quantity Frequency Duration Environment
    Unit Testing 100 to 1000+ Per build < 30s total Local and Build
    Acceptance Testing 10 to 100+ Per build < 10m total Local and Build
    Exploratory Testing 10 to 100+ Per build Timebox Local and 3rd Party
    Contract Testing ~20 Per 3rd party deploy < 1m 3rd Party
    Smoke Testing ~5 Per deploy < 5m All
    Monitoring 10 to 100+ Always < 10s All
    Anomaly detection 10 to 100+ < 1m < 10s All
    Adaptive architecture N/A Always N/A All

    Như vậy có thể thấy là việc xử lý Continuous Delivery gần như không mang tính kỹ thuật mà mang tính định hướng về văn hóa làm việc cho dự án nhiều hơn, chính vì thế tại sao mình lại đặt nó là Soft Skill.

  • Software Architecture: Bắt đầu từ đâu? – Part 2 Soft Skills – Decision

    Software Architecture: Bắt đầu từ đâu? – Part 2 Soft Skills – Decision

    Như ở phần trước mình đã nói về các Soft Skills mà mình cho là yếu tố khác biệt giữa Developer và Software Architecture.

    Decision

    Về cơ bản thì với vai trò là Software Architecture bạn sẽ cần quyết định nhiều thứ.

    Đầu tiên hãy kể đến chính Kiến trúc, thứ mà bạn đang xây dựng.

    Nếu bạn có vài trò là người quyết định kiến trúc, bạn gần như sẽ là người đảm bảo duy nhất cho sự toàn vẹn của hệ thống, về mặt khái niệm thôi nhé. Tức là bạn sẽ là người đầu tiên hình dung ra hình dáng của hệ thống và thông thường những quyết định kiểu này sẽ được đặt ra trong những phase đầu tiên của phần mềm.

    Nhiều người sẽ thắc mắc nếu quyết định đó là sai, thực tế thì những quyết định bởi những người có kinh nghiệm sẽ thường phù hợp với tình hình ở thời điểm đó.

    Một câu hỏi khác: tại sao chỉ một người đảm bảo duy nhất cho sự toàn vẹn? Thông thường ở một mức độ nào đó, trong một đội dự án, Architect sẽ có sự hiểu biết tốt nhất về hệ thống cũng như các hard skill khác, và họ sẽ có một cách hình dung khá chung với những người có trình độ tương đương. Tuy nhiên việc này sẽ dẫn tới việc nghẽn cổ chai ở Architect, vậy nên việc này sẽ dẫn tới một khái niệm khác là Architectural Knowledge Management hay Software Architecture Knowledge Management (điều này giải thích tại sao các team thường có wiki hay những bản tài liệu được chia sẻ về kiến thức) Read more

    Vậy thực sự có thể nói họ sẽ làm những việc như này

    • Thiết kế kiến trúc
    • Định ra các nguyên tắc để quyết định về mặt công nghệ.

    Ví dụ: Bạn sẽ cần đưa ra quyết định về việc sử dụng API Gateway để làm một endpoint duy nhất cho cả mobile và web hay sẽ sử dụng Backend for Frontend trong mô hình microservice.

    Ồ thì dẫn đến một điều bạn cứ trả lời 2 câu hỏi dưới đây bạn sẽ biết là bạn có đang đưa ra các quyết định về mặt kiến trúc hay không:

    • Mức độ ảnh hưởng có ở toàn hệ thống hay không? Độ khó để implement quyết định đó?
    • Quyết định đó có giúp team quyết định về mặt công nghệ hoặc chỉ định cho họ.

    Một công việc hằng ngày bạn có thể thấy mấy tên Architect hay làm đó là ngồi tìm hiểu thực sự bạn đang làm gì trong dự án, bạn có hiểu vấn đề chung của dự án hay không, tìm các vấn đề quan trọng và giải quyết trước khi mọi thứ trở nên tồi tệ (chỗ này định ghi là một bãi rác).

    Trong một dự án IoT gần đây mình có tham gia, Architect của dự án đã quyết định cho team backend dừng công việc để chỉnh trang lại kiến thực chung.

    Hoặc đơn giản hơn, Architect có thể ngồi với developer để tìm ra một số đoạn code gây lock hệ thống, buổi chiều có thể sharing với dự án về những thứ khá phi kỹ thuật như việc những đoạn code của họ sẽ gây ảnh hưởng đến chi phí AWS.

    Bạn có thể thấy rằng việc bạn không thể hình dung dự án đang làm ra sản phẩm như nào rất nguy hiểm, giả như rằng bạn khăng khăng bạn sẽ phải code lại toàn bộ luồng OAuth2.0 cho toàn bộ các microservice để dùng chung một endpoint, nhưng thực tế là hoàn toàn bạn có thể để chúng riêng rẽ, điều quan tâm duy nhất là dùng chung Identity Pool, việc làm đó có thể khiến những team khác trong dự án bị trễ lịch đến hàng tuần.

    Tính đúng đắn của quyết định — Justifying decisions

    Rõ ràng quyết định đã được đưa ra, điều đó là tất yếu trong hoàn cảnh đồng hồ của dự án bắt đầu tính giờ. Tuy nhiên thì không phải quyết định nào cũng được chấp nhận (chỉ là được chấp nhận nhé, chưa bàn đến tính đúng sai)

    Hãy quay trở lại ví dụ bên trên về API Gateway và Backend for Frontend, đây thực chất là kết quả sau khi Architect đã xem xét rõ ràng (đúng hoặc sai), còn quá trình thì sao.

    Để bất đầu hãy xem xét kịch bản tại sao lại có sự lựa chọn:

    Giả sử bạn đang xây dựng một cửa hàng trực tuyến sử dụng Microservice và bạn đang triển khai trang product detail. Bạn cần phát triển nhiều phiên bản của giao diện người dùng chi tiết sản phẩm: cho cả web browser dưới dạng HTML5 hoặc Rest API để phục vụ cho mobile.

    Giao diện này cần có nhiều thông tin: thông tin về sản phẩm, số lượng, lịch sử mua, các tùy chọn mua, các sản phẩm thường được mua cùng (một dang recommendation), review, rating của người bạn…

    Và chúng ta có các service sau:

    • Product Info Service
    • Pricing Service
    • Order service
    • Inventory service
    • Review service
    • Recommendation service Như vậy trang product detail cần tất cả các service trên

    Bài toán được đặt ra là:

    Làm cách nào để client của một ứng dụng Microservices truy cập vào các dịch vụ riêng lẻ?

    Quyết định

    Api Gateway

    Sử dụng API Gateway là entrypoint duy nhất cho tất cả các client. API Gateway xử lý các yêu cầu theo một trong hai cách.

    • Một số yêu cầu chỉ đơn giản là proxied/routed đến service thích hợp.
    • Hoặc thay vì cùng cấp API kiểu one-size-fits-all, API Gateway cung cấp các API khác nhau cho từng client. image

    Backend For Frontend

    BFF tạo ra các Gateway riêng rẽ cho từng loại Client. image

    Các điều kiện và ràng buộc (Conditions and Contraints)

    Có nhiều yếu tô cần tính tới trong trường hợp này, nhưng mình sẽ tạm thời bỏ qua những vấn đề mang tính chủ quan của một dự án phần mềm: Con người (mặt bằng chung của team, broken comb, T-Shaped, …), process, stakeholder (rất nhiều khách hàng can thiệp vào architecture), mặc dù trên thực tế Architect thường nắm vai trò Technical Lead luôn nên sẽ cần cân nhắc. Hãy đi vào vấn đề kỹ thuật thôi.

    • Thường thì API do microservices cung cấp thường khác với những gì Client cần, về chi tiết nhé. Microservices thường cung cấp các API chi tiết, có nghĩa là client sẽ cần tương tác với nhiều service. Ví dụ, như được mô tả ở trên, client cần thông tin chi tiết về sản phẩm và cần tới nhiều dịch vụ.
    • Các Client khác nhau sẽ cần dữ liệu khác nhau. Ví dụ phần Recommendation cho phiên bản web sẽ nhiều hơn và có nhiều hành động hơn phiên bản API cho Mobile.
    • Đường truyền điện thoại với 3G, 4G sẽ khác với các trình duyệt ở trên máy tính được kế nối qua cáp quang cả về băng thông lẫn tính ổn định, thậm chí là chi phí. Có nghĩa là chúng ta có thể tăng số lượng request từ phía trình duyệt mà không ảnh hưởng quá nhiều đến trải nghiệm của người dùng lẫn chi phí băng thông, ngược lại với ứng dụng mobile.
    • Số lượng phiên bản của service và thông tin (host:port) thay đổi động
    • Việc thay đổi các phiên bản của service (upgrade, canary) không nên transparent với client
    • Một số dịch vụ được integration và sử dụng gRPC, webservice hoặc SOAP không thân thiện lắm với các client hiện nay.

    Cân nhắc — Consideration

    Với API Gatway chúng ta sẽ có một API thân thiện với client với protocol tiêu chuẩn. Việc triển khai Client sẽ đơn giản hơn vì logic của việc xử lý nhiều service sẽ nằm ở Gateway thay vì client. Tuy nhiên dánh đổi bằng việc tăng độ phức tạp — API Gateway là một phần khác phải được phát triển, triển khai và quản lý, ngoài ra thời giản xử lý request cũng tăng lên vì thêm có bước xử lý network hop ở Gateway, tuy nhiên vân ngắn hơn thời gian xử lý cho BFF.

    Quyết định về kiến trúc — Architecture decision

    API Gateway

    Biện luận

    API Gatway với Spring có thể sử dụng Netflix Zuul, việc sử dụng Gateway sẽ đem đến trải nghiệm người dùng tốt hơn, vấn đề effort cho develop, deploy có thể giảm bằng cách đồng nhất cách thức xử lý cho các service sẽ đc xây dựng (dùng cùng một tech-stack)

    Tài liệu hóa và trao đổi về quyết định về kiến trúc

    Trao đổi về kiến trúc (giữa Architect và Developer)

    Trong dự án phần mềm của bạn có các kênh trao đổi nào?

    Email

    Email (đơn thuần là email) thực tế không thích hợp để trao đổi nhất là về mặt kỹ thuật, có quá nhiều vấn đề trong quá trình xây dựng kiến trúc và thông tin trong email khá khó để tổng hợp và truyền đạt lại, hãy nghĩ đến việc bạn sẽ cho một thành viên mới dành cả 2 tuần lễ để đọc lại thread mail.

    Hãy document quyết định về kiến trúc

    Sử dụng wiki là một biện pháp ổn, nhanh, giao diện đẹp, dễ viết với Markdown và quan trọng nhất là tập trung. Một vấn đề nữa, hãy chỉ rõ hoặc quyết định luôn chỗ mà quyết định được lưu trữ (hơi nhiều từ quyết định, tho)

    Với các vấn đề quan trọng và ảnh hưởng lớn, hãy đảm bảo tất cả mọi người cần biết được biết về nó. Bảng trắng luôn là một lựa chọn tốt để trao đổi, nếu trong thời buổi WFH có thể sử dụng các công cụ trong kênh giao tiếp.

    image

    P/S: hãy ra quyết định một cách khôn ngoan nhé.

    Happy Coding.

    to be continued sigange
  • Software Architecture: Bắt đầu từ đâu? – Part 1

    Software Architecture: Bắt đầu từ đâu? – Part 1

    Motivation

    Series này hưởng ứng Technopedia, nhưng các bài viết sẽ không phục vụ mục đích dự thi. Đầu tiên mình định viết về Solution Architecture theo quyển Solutions Architect’s Handbook nhưng nghĩ lại thì Software Architecture sẽ hợp lý để bắt đầu hơn.

    Disclaimer

    Bài viết này không nhằm mục đích hướng dẫn hay đề ra một con đường trong sự nghiệp, chỉ là chia sẻ và những gì mình đã trải nghiệm thôi.

    Developer đến Software Architecture

    Mọi người sẽ thắc mắc tại sao lại là Developer chứ không phải Coder. Về cơ bản thì Coder – Người code tức là họ chỉ biết hoặc cũng chỉ muốn code (Ở một phương diện nào đó), còn Developer – Người phát triển phần mềm, tức là họ xử lý tất cả các vấn đề liên quan đến quy trình phát triển phần mềm (Software Development Process) từ nói chuyển với stakeholders, thiết kế ứng dụng (ở một mức độ nào đó), deploy phần mềm, debug, sửa lỗi và cải tiến phần mềm. Một cách đơn giản trong đa số trường hợp Coder tương đương với Junior Developer.

    Ồ!!!

    Vậy Developer sẽ trở thành Software Architecture như nào và điểm khác biệt là gì khi developer đã có thể thiết kế ứng dụng (như mình có nói ở trên). Mình sẽ đưa ra ví dụ như này cho mọi người dễ hình dung:

    Trong một dự án xây dựng hệ thống và phần mềm di động đi kèm một chiếc máy ảnh, người dùng có thể download một số sticker và chỉnh sửa lên bức ảnh của mình, Backend sẽ xây dựng một vài API để phục vụ. Trong trường hợp này Developer đã có thể thiết kế được API, kiến trúc của Backend dựa trên ExpressJS, deploy chúng lên AWS, xử lý memcache để tốc độ phản hồi tốt hơn, người dùng nhận được sticker mới ngay khi chúng được upload.

    Vậy có thể thấy rằng hầu hết công việc đã được Developer xử lý (ở đây tạm thời không phân chia Junior-Middle-Senior nhé) và dự án golive thành công. 5 tháng sau đội dự án bị đập tơi bời từ khách hàng vì bill từ AWS vài $10000 cho mỗi tháng, trong trường hợp này nếu là bạn Software Architecture sẽ xử lý như nào

    (Thực ra có vài cách trong trường hợp này nhưng mình sẽ đưa ra một cách cho thấy sự tương phản nhất) Thay vì đưa trực tiếp API cho Mobile như bạn Software Developer, API sẽ được xử lý như một file tĩnh (JSON-XML) từ S3 cùng với sự hỗ trợ của CDN, file này sẽ được cập nhật khoảng 30′ một lần nếu có sự thay đổi về danh sách sticker. Như vậy người dùng sẽ chờ tối đa khoảng 30′ để có thể nhận được sticker mới, nhưng thay vào đó bill từ AWS sẽ chỉ khoảng $500.

    Vậy bạn Developer có thể biết cái gì tối ưu nhất cho hệ thống nhưng sẽ bị hạn chế khi xử lý các vấn đề tradeoffs-đánh đổi. Trong khi đó Architect sẽ cần biết cả 2.

    Software architecture?

    Đang nói về thiết kế ứng dụng chứ không phải job title nhé :P. Mình sẽ gạch đầu dòng thôi vì chỗ nào cũng có định nghĩa:

    • Các tiêu chuẩn, khái niệm tôn chỉ (gì gì đó) của một hệ thống.
    • Cách tổ chức, các component quan trọng, cách tương tác, những thành phần nhỏ hơn và interface giữa chúng (không cụ thể nhé).

    Về cơ bản thì Software architecture là những hiểu biết chung về thiết kế hệ thống trong dự án phần mềm, tất nhiên sẽ bao gồm cả việc hệ thống được chia nhỏ như nào, và interface giữa chúng là gì nữa, nhưng chỉ dừng lại ở những component và interface mà mọi người đều cần hiểu thôi nhé.

    Ví dụ: chi tiết về việc iOS team lưu trữ password hay load thông tin từ database ở local device như nào sẽ không là một phần trong architecture của cả dự án nhưng việc chỉ ra những gì cần lưu trữ ở local như một lớp caching thì lại có.

    Ông anh Martin Fowler thì định nghĩa khá đơn giản ở đây:

    So, this makes it hard to tell people how to describe their architecture. “Tell us what is important.” Architecture is about the important stuff. Whatever that is.

    Vậy cần những gì để trở thành một Software Architecture? Có rất nhiều bài viết nói về cái này, nhưng mình sẽ chỉ ra một số cái mình nghĩ là quan trọng nhất:

    • Hard skill hay technical skill gì gì đó:
      • Bao giờ cũng là các application architecture phổ biến, pattern, anti-pattern, cái này thì thực ra khá dễ để nhìn ra và dễ tiếp cận
      • Integration architecture, à thì không phải lúc nào cũng xây dựng một cái gì đó từ đầu (from scratch), nên integration luôn là sự lựa chọn tốt.
      • Enterprise architecture, nó liên quan đến tất cả mọi thứ của một công ty, cá thể kinh doanh, từ chiến lược kinh doanh, mô hình vận hành, hoạt động kinh doanh và cả những thứ liên quan đến IT và Infrastructure nữa, nên nếu có bị giới hạn về mặt hiểu biết, mình có thể sẽ không đi sâu về cái này.
    • Soft skill cái này quan trọng này, có thể mọi người sẽ nghĩ đến những thứ khá cao siêu như Leadership hay Organization nhưng mình sẽ đi từ những thứ nhỏ nhỏ trước:
      • Ra quyết định – Decisions, tất nhiên là bạn cần quyết định nhiều thứ, đặc biệt là về kiến trúc hoặc đơn giản hơn là trong team sẽ làm việc với nhau như nào (communication decision)
      • Continuous Delivery, nghe có vẻ là hard skill nhưng thực tế là soft skill. Về cơ bản phần mềm hay hệ thống sẽ vẫn là một thứ gì đấy trừu tượng (ở trên bản vẽ – mượn từ archiecture – kỹ sư trong ngành xây dựng), cho đến khi nó được deploy trên môi trường thực tế. Và khi phần mềm được deploy càng sớm và càng liên tục thì việc chứng mình architecture hợp lý càng nhanh và chính xác, mọi người cũng trưởng thành hơn về mặt thực hành.

    image

    Bài tiếp theo mình sẽ viết tiếp về các skill nhé.

  • GIT – Những lưu ý khi config user name/email cho repo

    GIT – Những lưu ý khi config user name/email cho repo

    Git và các dịch vụ Git như Github, Gitlab đang ngày càng trở nên phổ biến đối với các developer hiện này, hay có thể nói đó là phần không thể thiếu rồi.

    Và đương nhiên, một developer có thể contribute đến nhiều project/repository, và mỗi project có thể dùng một định danh khác nhau. Ví dụ như dùng account email công ty với những project của công ty, hoặc email cá nhân với những project làm thêm, học thêm. Thậm chí có những người được khách hàng cấp account riêng để truy cập vào git riêng của họ.

    Và nếu, developer không quản lý tốt git config cho từng project thì có thể sẽ bị nhầm lẫn email lúc commit lên. Đây là điều rất hay xảy ra nhưng tiếc là ít người để ý.

    Nếu bạn làm trong FSoft, hoặc các công ty đề cao việc security đối với việc dùng email công ty cho các mục đích ngoài công việc, thì có thể nhận được những email warning dạng: bạn đang dùng email công ty trong project git này….hãy cho biết lý do tại sao, và một loạt các câu hỏi khác.

    Tại sao IT lại biết được việc bạn push lên github, gần như ngay lập tức?

    Github có API public các event xảy ra trên dịch vụ này gần như realtime tại: https://api.github.com/events. Và nếu tôi viết 1 con Bot scan liên tục API này và phân tích data thì tôi dễ dàng tìm được thông tin người thực hiện commit đấy lên như email hay user name (đương nhiên là email và user name config thôi)

    Ví dụ như:

    Github public events

    Với sample phía trên, ta dễ dàng scan ra được user name và email của người vừa push lên. Và đương nhiên, con BOT của IT cũng sẽ tóm được ta nếu ta sử dụng email công ty để push lên.

    Tuy nhiên, nhiều người sẽ thắc mắc tại sao với project này, tôi sử dụng email cá nhân để đăng ký, nhưng thông tin push lên lại là email công ty?

    Câu trả lời rất đơn giản, nằm ở chỗ config của git mà thôi.

    Git có 2 loại config chính cho các repo là Global config và Local config. Với những thông tin mà được sử dụng ở tất cả các repo thì thường sẽ config trong Global config. Còn những thông tin riêng của từng project sẽ được config trong Local config của từng dự án.

    Tuy nhiên, rất nhiều developer quên, hoặc không biết đến điều này.

    Cùng xem ví dụ bên dưới.

    Ví dụ về config Git Global trong máy: (open terminal và gõ lệnh git config --list)

    Git global config sample

    Sau khi gõ lệnh thì ta sẽ lấy được tất cả thông tin mà git global config đang setting. (ấn q để thoát ra)

    Ví dụ về 1 project không có config user name và email (dùng lệnh git config --list --local)

    Với project/repo này thì khi commit lên, do thông tin user name và email bị thiếu, nêu git sẽ lấy config default trong Global config để thêm vào. Do đó, nếu bạn đã từng setting email global là email công ty, thì với commit này của bạn, email cũng sẽ là mail công ty, mặc dù repo này bạn không hề đăng ký bằng email công ty. Và IT sờ gáy.

    Vậy, làm thế nào để config thông tin cho từng repo? Ví dụ như dưới

    Cd vào thư mục source, dùng config:

    git config user.name nhathm
    git config user.email [email protected]

    Và như vậy, config đã được áp dụng cho project của bạn, check lại bằng command git config --list --local, nếu 2 dòng cuối cùng hiển thị thông tin đúng như bạn đã config là thành công

    user.name=nhathm
    [email protected]

    Hãy hết sức cẩn thận khi commit nếu như bạn đang tham gia nhiều project khác nhau, sẽ không tốt chút nào nếu repo cá nhân lại bị gắn email công ty, và ngược lại.

    Thanks.

  • Composition over Inheritance

    Composition over Inheritance

    Content

    • Vấn đề về sử dụng Inheritance
    • Disadvantages of Inheritance
    • Composition là gì?
    • Sử dụng Composition để thay thế Inheritance.

    Vấn đề về sử dụng Inheritance

    Inheritance là 1 trong những core concept của OOP. 1 vài lợi ích mà Inheritance mang lại đó là:

    • Code reusebility: Các lớp con có các properties và functions của lớp cha -> Có thể giảm sự duplicate code giữa các lớp con bằng cách đặt các phần code bị duplicate vào lớp cha.
    • Code dễ đọc và dễ hiểu hơn.
    • Inheritance đại diện cho mối quan hệ IS-A relation ship -> Các lớp con có thể thay thế cho lớp cha

    Mặc dù Inheritance được sử dụng rộng rãi, nhưng liệu nó có phải là 1 concept mạnh mẽ nên áp dụng mọi lúc mọi nơi? Hãy cùng suy ngẫm về 1 ví dụ sau:

    • SkyBird và MountainBird là 2 class kế thừa từ class Bird. SkyBird và MountainBird có chung hành vi eat() và fly(), do đó chúng ta đặt 2 phương thức này ở lớp Bird để reuse code.
    • Hành vi jug() là khác nhau nên chúng ta sẽ tự override lại ở lớp con.

    Nghe có vẻ ổn. Bây giờ, khách hàng muốn chúng ta thêm 2 class là WildBird và CloudBird. Chúng có hành vi fly() khác so với lớp Bird, vì vậy chúng ta sẽ override lại hành vi fly(), từ đó có thiết kế:

    Nhưng vấn đề ở đây là WildBird and CloudBird có cùng hành vi fly(). Nếu chúng ta thiết kế như Diagram ở trên thì chúng ta đã tạo ra 1 sự duplicate code hành vi fly giữua lớp WildBird và CloudBird.

    Bạn có thể nghĩ 1 cách để tránh duplicate code đó là tạo 1 class cha cho WildBird và CloudBird, và đặt hành vi fly() chung vào đó, như sau:

    Khá OK. Nhưng giờ khách hàng tiếp tục muốn 1 số hành vi khác như WildBird và SkyBird có chung hành vi eat() mới – khác hành vi eat của Bird, CloudBird và MoutainBird có chung hành vi jug(), …

    Nếu bạn tiếp tục cố gắng tạo ra các lớp cha mới thì bạn sẽ tạo ra 1 kiến trúc ngày càng mở rộng, càng rối cho hệ thống của bạn. Ngoài ra bạn còn phải sửa lại kiến trúc hiện có -> Dẫn đến sửa lại rất nhiều class, code hiện có (Vi phạm nguyên tắc OCP).

    Disadvantages of inheritance

    Giờ thì hãy cùng điểm qua 1 vài nhược điểm của kế thừa:

    • 1 object chỉ có thể kế thừa từ 1 lớp cha.
    • Dễ dàng tạo ra 1 kiến trúc lớn, phức tạp
    • Thay đổi 1 function ở lớp cha -> Ảnh hưởng tới toàn bộ lớp con.
    • Thường thì chúng ta cố gắng nhét tất cả các func chung vào lớp cha -> Dẫn đến lớp cha bị phình to, đồng thời có những func của lớp cha mà lớp con này không cần dùng đến. (Điều này rất dễ thấy trong chính project hiện tại của các bạn).
    • Khi mở rộng source code thường dẫn đến phải thay đổi các code hiện có.

    Composition là gì?

    Khác với Inheritance đại diện cho quan hệ IS-A giữa 2 class, thì Composition đại diện cho quan hệ HAS-A giữa 2 class:

    • Composition có nghĩa là "thành phần". Ở ví dụ trên, Wheel là 1 thành phần của Car, nói cách khác, Car chứa 1 instance kiểu Wheel.
    • Có thể hiểu Composition như việc xếp hình. Thay vì đặt các chức năng vào đối tượng gốc, bạn sẽ tách các chức năng ra thành các đối tượng riêng lẻ, sau đó ghép các thành phần đó lại để bổ sung thêm chức năng cho đối tượng gốc của bạn.
    Thay vì đặt các login run, start,… vào đối tượng gốc Car thì ta sẽ tách ra thành các thành phần riêng lẻ

    Lợi ích của Composition:

    • Giải quyết được vấn đề tạo ra 1 big hirachy khi dùng kế thừa.
    • Dễ dàng reuse code giữa các đối tượng 1 cách linh hoạt.
      Ví dụ: Ta có thể reuse các logic turnOn, turnOff giữa các loại xe mà không cần tạo class cha.
    • Khi thêm mới/ thay đổi các hành vi của đối tượng thì không cần phải thay đổi quá nhiều code hiện có, chỉ cần viết thêm code và thay thế các hành vi mới vào hành vi hiện tại. (Đảm bảo nguyên lí SRP và OCP).
    • Để reuse code mà có liên quan đến UI kéo thả thì sử dụng Inheritance là bất khả thi, còn Composition vẫn có thể giải quyết được 😉

    Áp dụng Composition để giải quyết vấn đề ở đầu bài viết.

    1. Tạo ra các interface cho các hành vi fly và eat lần lượt là IFlyBehaviour và IEatBehaviour.
    2. Các class FlyHighBehaviour và FlyLowBehaviour conform protocol IFlyBehaviour, chúng sẽ khai báo lại phương thức fly() với các hành vi riêng. Ngoài ra có thể tạo thêm các class có phương thức fly khác tùy theo yêu cầu bài toán.
    3. Class EatLeafBehaviour conform protocol IEatBehaviour, khai báo lại phương thức eat riêng. Ngoài ra có thể tạo thêm các class có phương thức eat khác tùy theo yêu cầu bài toán.
    4. WildBird vừa có thể bay và ăn nên nó sẽ chứa 2 instance kiểu IFlyBehaviour và IEatBehaviour; Penguin không thể bay nên chỉ cần chứa 1 instance kiểu IEatBehaviour.

    Code triển khai:

    Khi cần thêm các hành vi fly mới, chỉ cần viết thêm các class conform protocol IFlyBehaviour.
    Khi cần thêm các hành vi eat mới, chỉ cần viết thêm các class conform protocol IEatBehaviour.
    Ví dụ tạo 2 đối tượng WildBird có hành vi Fly khác nhau. Các loài chim với các hành vi fly/eat khác thì chỉ cần khởi tạo class chim đó với các hành vi mong muốn
    Khởi tạo đối tượng Penguin không có hành vi bay, và hành vi ăn là ăn cá.

    Kết luận:

    • Composition đem lại nhiều lợi ích hơn, tuy nhiên không phải là luôn luôn thay thế Inheritance = Composition.
    • Sử dụng Inheritance khi bạn thật sự cần dùng đến nó, chứ không nên chỉ vì mục đích reuse code.

    Reference

    https://betterprogramming.pub/inheritance-vs-composition-2fa0cdd2f939

    Cách sử dụng composition với protocol extension: https://medium.com/commencis/reusability-and-composition-in-swift-6630fc199e16 Cách này khá hay nhưng bị 1 nhược điểm là func của protocol không thể để là private.

  • Dev Container — Everyone should know in the Software development world

    Dev Container — Everyone should know in the Software development world

    Original Post: https://medium.com/techoverbygst/dev-container-everyone-should-know-in-software-development-world-ce028938ed16

    I started working with Docker and Container almost 7 or 8 years ago, but I focused 2 last year on architecting on the Cloud. Docker, known for the motto Build, Ship, Run, changed the Software development world a lot, especially web-based.

    Docker — Build, Ship, and Run

    We know that we can run software with Docker everywhere, and we make the development and production environment closer and closer, but I have thought: Can we make the development environment between developer and developer closer with Docker, even unify?

    I used Docker Compose in almost project; it’s worked well to make a database or messaging broker (Kafka) unified, but except for co-workers’ code, they tend to make the change locally and run it natively with their PC and Workstation, for e.g: run php artisan serve with Laravel or go build && go run with Golang. There are many pain points with many bugs because of so f**king many variables in their environment: Go Version, Php Version, Composer Version.

    Visual Studio Code Remote — Containerswill solve this problem. I know about it at random. I am trying to fork WikiJS for internal use wiki in GST, and WikiJS teams are using Remote Container.

    To run and update code, I have not to install specific required NPM, Database, Vue package, or even Elastic Search. It’s packed with a configured devcontainer.json and simple docker-compose.yml to define the environment and creating process.

    devcontainer link with docker-compose.yml, specific service wiki will be chosen as the development environment, postCreateCommand will run right after containers are created in Docker Compose

    We just need to install Remote Container extension in VSCode and reopen the current project in container view, Extension and Docker will do the rest.

    Wait for the create container process to finish.
    Then we have a fully installed dev environment and are ready to make a new code.

    Happy Coding!

  • How to write clean code (P1)

    How to write clean code (P1)

    Mặc dù lâu không viết gì vì đang bận làm đồ án, nhưng sau khi khi hoàn thành 1 task về refactor vài nghìn dòng code đã là động lực để em phải ngoi lên viết bài viết này, để chia sẻ về cách viết code mà bản thân đang áp dụng.
    Phần 1 của bài viết sẽ giới thiệu về cách cải thiện code đơn giản và dễ dàng nhất: Đặt tên

    Nội dung bài viết

    • Tầm quan trọng của clean code
    • Meaningful names

    Tầm quan trọng của clean code

    Tại sao clean code lại quan trọng?

    • Những dòng code cũng chính là 1 bản design document, vì vậy nếu code được viết 1 cách gọn gàng, dễ hiểu thì khi 1 người mới đọc code thì sẽ dễ dàng nắm được logic, flow của code – Sếp HoaND1 said.
    • Việc viết code 1 cách clean sẽ giảm thiểu thời gian dể người khác, hoặc chính bạn sau 1 thời gian đọc lại, có thể nhanh chóng hiểu được; tránh gây ra những hiểu lầm về mặt logic.
    Clean code cũng giúp tránh việc đồng nghiệp của bạn phải thốt lên “WTF” nữa

    Meaningful names

    Việc chọn 1 cái tên sao cho truyền tải đủ ý định của biến, hàm, class, … đôi khi sẽ mất nhiều thời gian, những nó sẽ tiết kiệm được nhiều thời gian hơn so với thời gian bị mất.
    Một cái tên tốt nên thể hiện tại sao biến này, hàm này, … tồn tại, nó có tác dụng thế nào, được sử dụng thế nào mà không cần tốn thêm quá nhiều thời gian để đi tìm hiểu chúng làm gì.

    let temp: Int = 0
    func check() -> Bool {}

    Ví dụ với những cái tên kiểu này, đồng nghiệp của bạn sẽ phải lặn lội mọi ngóc ngách nơi biến, hàm được gọi để có thể hình dung 1 cái nhìn mơ hồ xem chúng có tác dụng gì… Hãy biết thương đồng nghiệp của bạn.
    Ok, giờ thì đi tìm hiểu 1 vài cách đặt tên cho tốt

    Coding Conventions

    Trong swift, có 1 vài coding conventions cơ bản trong việc đặt tên như:

    • Tên biến, tên class nên là danh từ, tên hàm nên bắt đầu bằng 1 động từ
    • Sử dụng camel case (tránh dùng snakeCase)
    • Viết hoa chữ cái đầu cho các kiểu dữ liệu type, protocol, viết thường cho các thứ khác. …

    Hãy chọn những cái tên cụ thể với hành vi

    func sortListObject() {}

    Như tên hàm này không được cụ thể lắm, vì qua cái tên không thể hiện được là object sẽ sort theo kiểu gì?
    Nếu sort theo tên thì nên sửa lại thành sortListObjectById() chẳng hạn, hoặc sortListObjectByName() nếu sort theo tên.

    Nếu có điều gì quan trọng về 1 biến, 1 hàm mà người đọc nên biết, thì nên thêm thông tin đó vào tên.

    var id: String // "af84ef845cd8"
    -> var hexID: String

    Tránh những cái tên chung chung

    Những cái tên mơ hồ như temp, tmp, i, j, … trong đa số trường hợp thường không thể hiện được quá nhiều thông tin. Vì vậy, hãy chọn 1 cái tên truyền tải nhiều ý nghĩa hơn thay vì chúng.

    Cũng có 1 vài trường hợp những cái tên chung chung có thể chấp nhận. Ví dụ như:

    if left < right {
       let temp = left
       left = right
       right = left
    }

    Trong những trường hợp như thế này, việc sử dụng tên “temp” cũng ổn. Bởi mục đích của nó là lưu trữ tạm thời,với thời gian tồn tại chỉ vài dòng, nó cũng không có nhiệm vụ nào khác. Nó không được chuyển sang chức năng khác hoặc được đặt lại, hoặc sử dụng nhiều lần. Và quan trọng hơn là người khác vẫn có thể dễ dàng hiểu.

    Hãy chọn những cái tên ý nghĩa. Nếu bạn định sử dụng những cái tên chung chung như “temp”, “tmp”, … hãy chắc chắn rằng có 1 lí do hợp lí cho việc đó.

    Tránh những tên quá dài

    • Khi chọn tên, nên tránh những cái tên quá dài, bởi vì chúng rất khó đọc và khó để nhớ
    newNavigationControllerWrappingViewControllerForDataSourceOfClass
    • Đôi khi những cái tên cũng chứa những thông tin bị thừa, mà nếu bỏ đi thì cũng không ảnh hưởng gì tới ý nghĩa.
    func convertToString() 
    func toString() // Vẫn truyền tải đủ ý nghĩa

    Tránh những cái tên hiểu lầm

    • Tránh đặt những tên kiểu getXYZ(), sizeXYZ(), … nếu bên trong thân hàm xử lý những logic có độ phức tạp tính toán lớn, bởi những cái tên này thường dễ khiến người đọc hiểu lầm là hàm này "lightweight" nên dùng 1 cách thường xuyên.
    // Tên hàm gây hiểu lầm
    func getNumberOfSelectedObjects() -> Int {
       Loop hundreds time to calculate ...
    }
    • 1 vài kiểu gây hiểu lầm khác như đặt tên biến là objectIndexs nhưng lại trả về kiểu Object, …
    • Những cái tên trả về kiểu boolean thì tên biến, hàm nên bắt đầu bằng các từ như is, has, can, should, … để làm cho rõ nghĩa.
    • 1 số cái tên thông dụng khác như max, min cho giới hạn; first, last cho thứ tự,…

    Kết luận

    Hãy cố gắng chọn những cái tên truyền đạt đầy đủ ý nghĩa và dễ hiểu đối với mọi người.

    Tham khảo

    Sách "The Art of Readable Code" – O’Reilly
    Sách "Clean Code" – Uncle Bob

  • Triển khai CI/CD với GitLab-CI (P1 – Cài đặt và khởi tạo)

    Triển khai CI/CD với GitLab-CI (P1 – Cài đặt và khởi tạo)

    Chú ý 1: Phạm vi của bài viết bao gồm việc triển khai CI/CD với GitLab-CI trực tiếp trên thiết bị MacOS, không sử dụng container

    Chú ý 2: Do nội dung của phần này hơi nhiều nên mình xin phép tách thành nhiều bài nhỏ để mọi người đọc đỡ bị mệt mỏi và dễ focus vào các phần muốn đọc hơn

    Bỏ qua các triết lý hay định nghĩa về CI/CD, mình xin phép đề cập luôn đến việc triển khai CI/CD . Để triển trai CI/CD với GitLab-CI, chúng ta cần làm các bước sau:

    1. Cài đặt gitlab-runner trên thiết bị node (MacOS, Window)
    2. Khởi tạo runner liên kết với GitLab Repository
    3. Cấu hình GitLab Runner với config.toml (optional)
    4. Cấu hình các jobs CI/CD với .gitlab-ci.yml

    1. Cài đặt gitlab-runner trên thiết bị node

    Để cài đặt gitlab-runner trên máy, chạy command sau trên terminal:

    > brew install gitlab-runner

    Ngoài ra, các bạn có thể tham khảo quá trình cài đặt trên doc của GitLab (https://docs.gitlab.com/runner/install/)

    Sau khi cài xong, bạn có để sử dụng command sau để kiểm tra phiên bản hiện tại trên máy:

    > gitlab-runner verify

    2. Khởi tạo runner liên kết với GitLab Repository

    Chú ý: Để truy cập các chức năng liên quan đến cài đặt GitLab-CI, hãy đảm bảo tài khoản được cấp quyền Maintainer hoặc cao hơn.

    Đầu tiên, chúng ta truy cập GitLab Repo cần chaỵ CI/CD, sau đó chọn Settings -> CI/CD -> Expand section Runners.

    Thông tin để đăng ký Runner sẽ hiển thị ở đây

    Ở đây, hãy chú ý đến hai giá trị là gitlab-ci coordinatorgitlab-ci token

    Sau đó. ta dùng command sau để bắt đầu khởi tạo Runner:

    > gitlab-runner register

    Tiếp đó, ta nhập các thông tin của Runner theo hướng dẫn

    Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/):
    > https://gitlab.techover.io/ (gitlab-ci coordinator ở trên kia)
    Please enter the gitlab-ci token for this runner:
    > oH5aKzZsqibPr******** (gitlab-ci token ở trên kia)
    Please enter the gitlab-ci description for this runner:
    > Techover IO Runner (Mô tả cho runner)
    Please enter the gitlab-ci tags for this runner (comma separated):
    > develop (gắn tag cho runner)
    Please enter the executor: ssh, virtualbox, docker+machine, kubernetes, parallels, shell, docker-ssh, docker-ssh+machine, custom, docker:
    > shell (tuỳ chọn executor, ở đây mình sẽ dùng shell)
     Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!  

    Như vậy là đã đăng ký xong runner với GitLab Repo, giờ chúng ta sẽ start runner bằng command sau:

    > gitlab-runner start 

    Reload lại page GitLab, ta sẽ thấy Runner xuất hiện trong danh sách Runners của Repo. Như vậy là đã hoàn thành việc khởi tạo runner cho Repo.

    3. Cấu hình GitLab Runner với config.toml (Optional)

    Bạn có thể skip qua phần 3 nếu không hứng thú, vì nếu không làm thì GitLab-Ci vẫn chạy bình thường, mà ở đây mình cũng chỉ giới thiệu qua thôi 😀 chi tiết mình sẽ cố gắng làm trong một phần khác trong tương lai . File config.toml bao gồm các cài đặt, tuỳ chỉnh cho runner chạy trên thiết bị. Ta có thể truy cập file config.toml với đường dẫn sau:

    > ~/.gitlab-runner/config.toml 

    Tại đây bạn có thể tuỳ chỉnh rất nhiều các Các chi tiết về việc cài đặt có thể tham khảo trên doc của GitLab: https://docs.gitlab.com/runner/configuration/advanced-configuration.html

    Ví dụ ở đây mình thêm cài đặt giới hạn size của file log lên 40MB (Mặc định là 4MB)

    4. Cấu hình các jobs CI/CD với .gitlab-ci.yml

    GitLab-CI không có cấu hình mặc định cho CI/CD, để cấu hình ta cần thêm file .gitlab-ci.yml vào thư mục gốc của Repo. File sử dụng YAML, có thể tham khảo cú pháp ở link sau: https://docs.gitlab.com/ce/ci/yaml/

    Dưới đây là ví dụ template file .gitlab-ci.yml của GitLab cho Repository sử dụng Swift

    # This file is a template, and might need editing before it works on your project.
    # Lifted from: https://about.gitlab.com/2016/03/10/setting-up-gitlab-ci-for-ios-projects/
    # This file assumes an own GitLab CI runner, setup on a macOS system.
     stages:
       - build
       - test
       - archive
       - deploy
     
    
     build_project:
       stage: build
       script:
         - xcodebuild clean -project ProjectName.xcodeproj -scheme SchemeName | xcpretty
         - xcodebuild test -project ProjectName.xcodeproj -scheme SchemeName -destination 'platform=iOS Simulator,name=iPhone 8,OS=11.3' | xcpretty -s
       tags:
         - ios_11-3
         - xcode_9-3
         - macos_10-13
     
    
     archive_project:
       stage: archive
       script:
         - xcodebuild clean archive -archivePath build/ProjectName -scheme SchemeName
         - xcodebuild -exportArchive -exportFormat ipa -archivePath "build/ProjectName.xcarchive" -exportPath "build/ProjectName.ipa" -exportProvisioningProfile "ProvisioningProfileName"
       only:
         - master
       artifacts:
         paths:
           - build/ProjectName.ipa
       tags:
         - ios_11-3
         - xcode_9-3
         - macos_10-13 

    Để apply các cài đặt trong file .gitlab-ci.yml, ta chỉ cần đẩy file lên origin. Sau đó Runner sẽ tự động nhận và chạy jobs theo những gì đã định nghĩa trong file.

    Để kiểm tra lịch sử chạy các Pipeline, chúng ta truy cập GitLab Repo chaỵ CI/CD, sau đó chọn CI/CD .

    Vậy là chúng ta đã hoàn tất quá trình cài đặt và khởi tạo Gitlab runner cho một Repository. Ở các bài viết tiếp theo, mình sẽ nói về việc cài cắm pipeline để chạy CI/CD cho các dự án thực tế. Xin cảm ơn các bạn đã theo dõi 😀