<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>AI Engineering on miti99</title><link>https://miti99.com/tags/ai-engineering/</link><description>Recent content in AI Engineering on miti99</description><generator>Hugo -- gohugo.io</generator><language>vi</language><lastBuildDate>Wed, 24 Jun 2026 18:14:21 +0700</lastBuildDate><atom:link href="https://miti99.com/tags/ai-engineering/index.xml" rel="self" type="application/rss+xml"/><item><title>Newsletter #113</title><link>https://miti99.com/post/2026/06/24/</link><pubDate>Wed, 24 Jun 2026 00:00:00 +0700</pubDate><guid>https://miti99.com/post/2026/06/24/</guid><description>&lt;p&gt;&lt;em&gt;Mời bạn thưởng thức Newsletter #113.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="di-chuyển-từ-go-sang-rust-chọn-đúng-lý-do-và-đúng-phạm-vi"&gt;&lt;a class="link" href="https://corrode.dev/learn/migration-guides/go-to-rust/" target="_blank" rel="noopener"
&gt;Di chuyển từ Go sang Rust: chọn đúng lý do và đúng phạm vi&lt;/a&gt;
&lt;/h2&gt;&lt;p&gt;Matthias Endler viết một hướng dẫn dài cho các nhóm backend đang cân nhắc chuyển dịch vụ từ Go sang Rust. Điểm chính không phải là Rust luôn nhanh hơn Go, mà là Rust đưa nhiều rủi ro vào hệ thống kiểu: &lt;code&gt;nil&lt;/code&gt;, xử lý lỗi, data race, lifetime tài nguyên và ownership. Go tối ưu cho tốc độ triển khai, compile nhanh, standard library mạnh và mô hình goroutine dễ dùng; Rust đổi lại bằng compiler nghiêm ngặt hơn, không có GC, &lt;code&gt;Result&lt;/code&gt;, &lt;code&gt;Option&lt;/code&gt;, trait và kiểm tra &lt;code&gt;Send&lt;/code&gt;/&lt;code&gt;Sync&lt;/code&gt; để bắt nhiều lỗi trước production.&lt;/p&gt;
&lt;p&gt;Phần thực tế nhất là chiến lược migration: không rewrite toàn bộ. Hãy bắt đầu từ hot path, worker, service có boundary rõ, hoặc route từng endpoint qua gateway theo kiểu strangler pattern. &lt;code&gt;cgo&lt;/code&gt; có thể dùng nhưng thường làm backend phức tạp hơn so với tách Rust thành service riêng. Tác giả cũng nhắc các chi phí thật: borrow checker khiến tháng đầu khó hơn, compile chậm hơn, async coloring và một số niche ecosystem nhỏ hơn. Vì vậy Go vẫn hợp cho Kubernetes tooling, CLI, glue service và hệ thống cần tốc độ team; Rust đáng giá hơn với foundational service nơi reliability, P99 latency và các lỗi như nil/data race tạo chi phí vận hành lớn.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Điểm chính:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rust migration đáng giá khi compile-time guarantees giảm incident nhiều hơn chi phí học, build và vận hành.&lt;/li&gt;
&lt;li&gt;Các pattern quen thuộc trong Go cần được map lại: &lt;code&gt;if err != nil&lt;/code&gt; thành &lt;code&gt;Result&lt;/code&gt;/&lt;code&gt;?&lt;/code&gt;, &lt;code&gt;nil&lt;/code&gt; thành &lt;code&gt;Option&lt;/code&gt;, interface thành trait, goroutine thành task khi thật sự cần.&lt;/li&gt;
&lt;li&gt;Nên bắt đầu từ service độc lập, hot path hoặc worker có contract rõ; giữ nguyên API để chuyển traffic từng phần.&lt;/li&gt;
&lt;li&gt;Không cần bỏ Go hoàn toàn; dùng Go cho phần &amp;ldquo;boring&amp;rdquo; và Rust cho phần cần độ tin cậy hoặc hiệu năng cao hơn.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="ai-engineering-cho-lập-trình-viên-đã-biết-ship-phần-mềm"&gt;&lt;a class="link" href="https://www.lucavallin.com/blog/ai-engineering-for-developers" target="_blank" rel="noopener"
&gt;AI Engineering cho lập trình viên đã biết ship phần mềm&lt;/a&gt;
&lt;/h2&gt;&lt;p&gt;Luca Cavallin viết một bài hướng dẫn rất dài về AI engineering cho developer đã quen backend, HTTP, queue, Kubernetes và production. Luận điểm chính là khi đưa LLM vào sản phẩm, mô hình không còn là toàn bộ sản phẩm; sản phẩm thật là hệ thống bao quanh mô hình: prompt, RAG, eval, agent, inference, observability, bảo mật và chi phí. Khác với API truyền thống, output của AI không nhị phân đúng/sai và có thể thay đổi theo model, prompt, dữ liệu hoặc ngữ cảnh người dùng, nên cần eval dataset, regression check và monitoring ngay từ đầu.&lt;/p&gt;
&lt;p&gt;Bài viết đi từ nền tảng model tới các phần thực dụng hơn: chọn model theo task/cost/latency, dùng prompt engineering trước khi RAG, dùng RAG như một bài toán search có hybrid retrieval và reranking, finetune chỉ khi prompt/RAG đã hết tác dụng, tối ưu inference bằng caching/batching/model routing, rồi thiết kế agent như một vòng lặp có tool call, quyền hạn và trace rõ ràng. Phần production nhấn mạnh context enhancement, guardrails, CI/CD riêng cho prompt và model change, cost attribution theo feature, cùng nguyên tắc xem mỗi agent như một service có identity, quyền tối thiểu và audit log.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Điểm chính:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AI engineering gần với backend engineering hơn ML training: nhiệm vụ chính là orchestration, grounding, eval, observability và vận hành.&lt;/li&gt;
&lt;li&gt;Không nên ship AI feature chỉ bằng demo; cần eval dataset, quality checkpoint, canary và theo dõi regression theo mỗi lần đổi prompt hoặc model.&lt;/li&gt;
&lt;li&gt;RAG chủ yếu là bài toán retrieval; hybrid search, reranker, chunking tốt và trích dẫn nguồn quan trọng hơn việc &amp;ldquo;nhét thêm context&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;Agent cần quyền hạn hẹp, tool schema rõ, tracing xuyên biên giới và approval gate cho hành động nguy hiểm.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="kỹ-thuật-vô-hình-phía-sau-mạng-của-aws-lambda"&gt;&lt;a class="link" href="https://www.allthingsdistributed.com/2026/04/the-invisible-engineering-behind-lambdas-network.html" target="_blank" rel="noopener"
&gt;Kỹ thuật vô hình phía sau mạng của AWS Lambda&lt;/a&gt;
&lt;/h2&gt;&lt;p&gt;Bài viết trên All Things Distributed kể lại gần một thập kỷ tối ưu hạ tầng mạng phía sau AWS Lambda. Vấn đề ban đầu là cold start của Lambda khi kết nối vào VPC: ngoài việc tạo microVM Firecracker, tải mã và khởi động runtime, hệ thống còn phải dựng đường mạng riêng để đi vào VPC của khách hàng. Một phần độ trễ đến từ Geneve tunnel và DHCP; riêng việc tạo tunnel từng nằm trên hot path và có thể tạo thêm hàng trăm mili giây. Đội Lambda chọn eBPF thay vì vá kernel riêng: tạo tunnel trước với VNI giả, rồi dùng eBPF rewrite header khi VNI thật xuất hiện. Độ trễ tunnel giảm từ khoảng 150ms xuống 200 micro giây.&lt;/p&gt;
&lt;p&gt;Phần hay hơn là cách họ xử lý vấn đề ở quy mô 4.000 network trên mỗi worker. Thay vì tạo tap, veth, namespace và rule khi function được gọi, họ pre-create toàn bộ khi worker khởi động, biến chi phí biến thiên thành chi phí cố định. Họ thay stateful NAT bằng eBPF stateless packet mangling, giảm NAT setup latency 100 lần; chuyển hơn 125.000 iptables rule ở root namespace thành 144 rule tĩnh bằng cách đưa rule theo slot vào namespace riêng; rồi giảm nghẽn RTNL lock bằng cách đổi thứ tự tạo network và batch attach eBPF. Kết quả là một topology thống nhất cho workload truyền thống và SnapStart, tăng capacity snapshot network 20 lần và còn được đóng gói lại để Aurora DSQL dùng chung.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Điểm chính:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tối ưu hạ tầng lớn thường là công việc &amp;ldquo;vô hình&amp;rdquo;: khách hàng chỉ thấy Lambda khởi động nhanh hơn và ổn định hơn.&lt;/li&gt;
&lt;li&gt;eBPF giúp Lambda đưa tunnel setup khỏi hot path mà không cần duy trì kernel patch riêng.&lt;/li&gt;
&lt;li&gt;Pre-create network khi worker boot là ví dụ rõ của constant work: trả chi phí một lần thay vì trả liên tục theo tải.&lt;/li&gt;
&lt;li&gt;Ở quy mô lớn, iptables rule, conntrack và RTNL lock đều có thể trở thành bottleneck hệ thống.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="chạy-test-chọn-lọc-ở-stripe-ci-nhanh-cho-monorepo-ruby-50-triệu-dòng"&gt;&lt;a class="link" href="https://stripe.dev/blog/selective-test-execution-at-stripe-fast-ci-for-a-50m-line-ruby-monorepo" target="_blank" rel="noopener"
&gt;Chạy test chọn lọc ở Stripe: CI nhanh cho monorepo Ruby 50 triệu dòng&lt;/a&gt;
&lt;/h2&gt;&lt;p&gt;Aditya Anchuri mô tả cách Stripe giữ CI đủ nhanh cho một monorepo Ruby khoảng 50 triệu dòng, với gần 100.000 test file và 1,2 triệu test unit. Nếu chạy tuần tự toàn bộ suite, một build sẽ mất khoảng bốn tháng, trong khi Stripe chạy khoảng 50.000 build mỗi tuần. Hệ thống Selective Test Execution (STE) giải quyết bằng cách chỉ chạy trung bình khoảng 5% test suite cho mỗi build, median còn dưới 0,5%, và dùng dưới 10% compute so với chiến lược luôn chạy tất cả.&lt;/p&gt;
&lt;p&gt;Điểm hay là Stripe không cố làm static dependency analysis hoàn hảo cho Ruby, vì metaprogramming, dynamic dispatch, config, fixture và artifact sinh ra khiến phân tích tĩnh dễ thiếu hoặc quá bảo thủ. Thay vào đó, họ quan sát runtime: một thư viện C++ nội bộ được nạp bằng &lt;code&gt;LD_PRELOAD&lt;/code&gt; intercept các lần mở file, gắn file được đọc với scope của test đang chạy, rồi truyền tracing sang child process. Log thô được gom thành selection index dạng roaring bitmap, map từ file đã thay đổi sang danh sách test cần chạy. Việc phát hiện thay đổi dùng &lt;code&gt;hashdeep&lt;/code&gt; để bao phủ cả file sinh ra, không chỉ file trong git. Stripe cũng thêm guardrail thực tế: luôn chạy lại test đang fail, luôn chạy một số test dùng glob/file discovery, xử lý linter theo danh sách file đổi, và lưu metadata baseline trong MongoDB với Monotonic Revision ID để chọn dữ liệu nền nhanh và tái lập được.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Điểm chính:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Với ngôn ngữ động như Ruby, quan sát file access lúc chạy có thể đáng tin hơn việc suy luận dependency bằng phân tích tĩnh.&lt;/li&gt;
&lt;li&gt;Instrumentation phải rẻ: trong đường &lt;code&gt;open&lt;/code&gt; chỉ ghi log tối thiểu, còn aggregation và indexing nằm ngoài hot path.&lt;/li&gt;
&lt;li&gt;Roaring bitmap giúp lưu khoảng ba tỷ điểm dữ liệu mà vẫn truy vấn nhanh khi union các test bị ảnh hưởng.&lt;/li&gt;
&lt;li&gt;STE không chỉ là thuật toán chọn test; nó cần baseline rõ ràng, dữ liệu tái lập được và guardrail cho các edge case của CI thật.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="oncall-đã-dạy-tôi-mọi-thứ"&gt;&lt;a class="link" href="https://yaoyue.org/blog/2026-oncall/" target="_blank" rel="noopener"
&gt;Oncall đã dạy tôi mọi thứ&lt;/a&gt;
&lt;/h2&gt;&lt;p&gt;Yao Yue nhìn lại 7,5 năm trực oncall cho hệ thống distributed caching ở Twitter, một trong những dịch vụ có throughput cao nhất và cũng nhiều sự cố nghiêm trọng nhất. Bài viết không tô hồng oncall, nhưng lập luận rằng chính việc sống cùng production đã tạo nên tư duy kỹ sư hạ tầng của tác giả. Oncall dạy rằng hệ thống lớn không chỉ cần nhanh ở median; điều quan trọng hơn là tính dự đoán được, tail latency, kiến trúc đủ rõ để xử lý khủng hoảng, observability tốt, cấu hình nhất quán, automation sẵn sàng và default hợp lý ngay từ thiết kế.&lt;/p&gt;
&lt;p&gt;Phần đáng nhớ hơn là bài học về con người. Việc restart Memcached để tránh meltdown đôi khi lại tự gây sự cố cho toàn site, nên tác giả học cách chọn thời điểm ít rủi ro, thông báo trước và quan trọng nhất là nhận trách nhiệm khi mắc lỗi. Nhiều incident chỉ được giải quyết nhờ sự kiên trì thu hẹp khả năng lỗi và nhờ đồng nghiệp từ nhiều mảng hạ tầng cùng giúp: ops, kernel, service upstream, manager và teammate cùng trực chiến. Kết luận của bài viết rất thẳng: kỹ sư không thật sự hiểu phần mềm cho tới khi nhìn nó chạy và hỏng trong production; người viết phần mềm không nên nghĩ mình đứng ngoài việc triển khai, giám sát và debug chính thứ mình tạo ra.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Điểm chính:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Oncall biến các khái niệm như tail latency, dependency, observability và rollback thành thực tế có hậu quả.&lt;/li&gt;
&lt;li&gt;Operational excellence phải được thiết kế từ đầu, không phải vá vội trước launch.&lt;/li&gt;
&lt;li&gt;Nhận trách nhiệm khi gây sự cố là cách xây lại niềm tin nhanh hơn việc né tránh lỗi.&lt;/li&gt;
&lt;li&gt;Production là nơi kỹ sư học sâu nhất về cả hệ thống lẫn cách phối hợp với con người.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="claude-như-cộng-sự-phân-tích-hiệu-năng"&gt;&lt;a class="link" href="https://developers.redhat.com/articles/2026/05/29/claude-your-performance-analysis-partner" target="_blank" rel="noopener"
&gt;Claude như cộng sự phân tích hiệu năng&lt;/a&gt;
&lt;/h2&gt;&lt;p&gt;Archana Ravindar thử dùng Claude để hỗ trợ phân tích hiệu năng Go qua CPU profile và trace, tập trung vào Green Tea garbage collector trên POWER10 với bộ benchmark &lt;code&gt;sweet&lt;/code&gt;. Điểm mạnh của Claude không phải là tự động tạo bản vá đúng ngay, mà là giúp đọc nhanh những file rất lớn và khó nhìn: &lt;code&gt;pprof&lt;/code&gt;, assembly dump và trace timeline. Ví dụ, Claude chỉ ra hot path trong &lt;code&gt;runtime.tryDeferToSpanScan&lt;/code&gt;, phát hiện mẫu atomic &lt;code&gt;Load8&lt;/code&gt; rồi &lt;code&gt;Or8&lt;/code&gt;, đề xuất gộp thành &lt;code&gt;Or32&lt;/code&gt;, sau đó cũng giúp giải thích vì sao thử nghiệm này lại regression do false sharing và contention giữa các worker GC.&lt;/p&gt;
&lt;p&gt;Bài viết cũng cho thấy Claude hữu ích khi phân tích mức thấp: đối chiếu assembly để thấy compiler không cache lại &lt;code&gt;q.class.sizeclass&lt;/code&gt;, ưu tiên TODO trong GC theo độ nóng của profile, gợi ý pattern cần nhìn trong trace như idle processor khi GC, mark assist gap, GC quá thường xuyên, hoặc &lt;code&gt;gcMarkDone&lt;/code&gt; dài. Khi so sánh nhiều trace/profile, Claude có thể tóm tắt benchmark nào hưởng lợi từ Green Tea GC và benchmark nào bị bottleneck bởi phần khác. Nhưng tác giả nhấn mạnh mọi gợi ý phải được validate: tối ưu phụ thuộc kiến trúc, cache line, ISA hoặc register pressure rất dễ sai nếu thiếu ngữ cảnh.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Điểm chính:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Claude hữu ích như công cụ exploratory analysis cho &lt;code&gt;pprof&lt;/code&gt;, trace và assembly, đặc biệt khi dữ liệu quá lớn để đọc thủ công.&lt;/li&gt;
&lt;li&gt;Gợi ý tối ưu cấp thấp phải đo lại; ví dụ giảm số atomic instruction vẫn có thể chậm hơn vì false sharing.&lt;/li&gt;
&lt;li&gt;Profile cho biết chi phí nằm ở đâu; trace giúp hiểu vì sao hệ thống bị nghẽn, đặc biệt với GC và scheduling.&lt;/li&gt;
&lt;li&gt;Dùng Claude tốt nhất là để thu hẹp vùng nghi vấn, so sánh regression và tạo checklist điều tra, không phải thay thế benchmark.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="những-điều-có-thể-bạn-chưa-biết-về-index"&gt;&lt;a class="link" href="https://jon.chrt.dev/2026/04/15/things-you-didnt-know-about-indexes.html" target="_blank" rel="noopener"
&gt;Những điều có thể bạn chưa biết về index&lt;/a&gt;
&lt;/h2&gt;&lt;p&gt;Jon Charter giải thích index trong Postgres bằng ví dụ rất dễ hiểu: index giống mục lục sách, giúp database tìm dữ liệu bằng cấu trúc đã sắp xếp thay vì scan toàn bảng. Nhưng bài viết nhấn mạnh trade-off quan trọng: đọc nhanh hơn, ghi chậm hơn. Mỗi &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt; phải cập nhật thêm index; index cũng tốn dung lượng, cache và làm query planner có nhiều phương án hơn để cân nhắc. Vì vậy &amp;ldquo;index tất cả mọi thứ&amp;rdquo; thường không phải chiến lược tốt.&lt;/p&gt;
&lt;p&gt;Phần hữu ích nhất là các lỗi phổ biến khiến index không được dùng. Composite index phụ thuộc thứ tự cột: &lt;code&gt;(type_1, type_2)&lt;/code&gt; giúp query theo &lt;code&gt;type_1&lt;/code&gt; hoặc cả hai cột, nhưng không giúp nhiều nếu chỉ lọc &lt;code&gt;type_2&lt;/code&gt;. Hàm trên cột như &lt;code&gt;lower(name)&lt;/code&gt; cũng làm index thường trên &lt;code&gt;name&lt;/code&gt; vô dụng, vì database cần cấu trúc đã sắp xếp theo chính biểu thức &lt;code&gt;lower(name)&lt;/code&gt;; implicit conversion cũng có thể gây hiệu ứng tương tự. Cách kiểm tra đúng là dùng &lt;code&gt;EXPLAIN&lt;/code&gt; hoặc &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt;, không đoán. Bài viết cũng giới thiệu functional index, partial index cho những lát dữ liệu nhỏ như soft-delete hoặc &lt;code&gt;is_legendary = true&lt;/code&gt;, và covering index/&lt;code&gt;INCLUDE&lt;/code&gt; để một số query có thể chạy bằng &lt;code&gt;Index Only Scan&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Điểm chính:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Index tăng tốc đọc nhưng làm ghi, cache và planning phức tạp hơn.&lt;/li&gt;
&lt;li&gt;Thứ tự cột trong composite index phải đi theo pattern query thật.&lt;/li&gt;
&lt;li&gt;Function, expression và implicit conversion có thể khiến index hiện có bị bỏ qua.&lt;/li&gt;
&lt;li&gt;Dùng &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt; để xác nhận planner thật sự chọn index nào trước khi tối ưu tiếp.&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>