Developer experience thường bị hiểu là “công cụ đẹp” hoặc “README dài”. Nhưng DevEx tệ không phải vì thiếu tool, mà vì feedback loop quá dài. Sửa một dòng code → chạy test local 8 phút → push → CI chạy 25 phút → review chờ 2 ngày → merge → deploy staging mất 1 giờ vì pipeline broken. Từ ý tưởng đến chứng cứ (code chạy đúng trên môi trường gần production) mất 3 ngày, cho một thay đổi 10 dòng.
Khi feedback loop ngắn, dev tự tin commit nhỏ, release thường xuyên, phát hiện bug sớm. Khi feedback loop dài, dev gom thay đổi lớn, test ít hơn, release ít hơn, batch risk lớn hơn. DevEx không phải nice-to-have, nó ảnh hưởng trực tiếp đến tốc độ và chất lượng ship code.
Bài này nhìn DevEx như hệ thống có thể đo và cải thiện, từ xác định bottleneck trong pipeline đến roadmap 90 ngày thực tế cho team 5-50 engineer.
Pipeline feedback loop
Vòng lặp cơ bản của dev:
edit → local test → push → CI → review → merge → deploy (staging/prod)
Mỗi bước có thể là bottleneck, và bottleneck ở mỗi bước cần giải pháp khác nhau.
Local test chậm: dev không chạy test trước khi push → CI đỏ → rework → thêm một vòng CI. Có những team test suite local chạy 12 phút, gần như không ai chạy trước push. Test trên CI trở thành “nơi phát hiện bug” thay vì “nơi xác nhận”. Fix: tách test nhanh (unit, < 30 giây) khỏi test chậm (integration), chạy fast suite trước push.
CI flake: test pass rồi fail rồi pass lại khi retry, không ai tin CI đỏ là bug thật. Culture “retry until green” lan rộng → bug thật lọt qua vì mọi người assume đỏ là flake. Flake là nợ lãi kép, tích lũy theo thời gian, ăn mòn confidence vào pipeline.
Review treo: PR mở 3 ngày chưa có review → WIP tăng, branch diverge, conflict tăng. Dev phải context switch khi reviewer cuối cùng comment. Fix: team agreement “review trong 24 giờ”, PR nhỏ (dễ review nhanh).
Deploy lâu hoặc không repeatable: deploy staging mất 1 giờ hoặc fail random → ít release → gom change lớn → risk mỗi release tăng.
Đo bottleneck trước khi mua tool
Thường thấy nhiều team mua CI tool mới, thêm dashboard, setup platform trước khi biết bottleneck ở đâu. Đo trước, đầu tư sau.
Bốn chỉ số thực dụng nên track:
Thời gian CI median/P90, chi phí mỗi push. CI 5 phút vs 30 phút là khác biệt lớn về flow dev. Target: dưới 10 phút cho fast path.
Tỉ lệ flake, job pass sau retry / total retry. Flake rate 20% nghĩa là cứ 5 lần CI đỏ thì 1 lần là real bug, 4 lần là noise. Không ai tin CI đỏ nữa.
Thời gian open PR → merge, throughput review. Median 4 giờ là tốt, 3 ngày là bottleneck.
Thời gian merge → staging có artifact, tốc độ “gần prod”. Nếu merge xong mà staging deploy mất 2 giờ, feedback vẫn chậm.
Lấy số liệu 2 tuần. Thường bottleneck rõ ràng, CI chậm, hoặc review treo, hoặc flake nhiều. Fix bottleneck lớn nhất trước.
Cache, hermetic build, golden path
Remote cache build
Build from scratch mỗi PR CI là waste. Remote cache (Bazel, Gradle build cache, tsc incremental, Turborepo) giảm trùng lặp, nếu code module A không đổi, dùng cached artifact thay vì rebuild. Có team CI từ 25 phút xuống 8 phút chỉ bằng bật Gradle remote cache.
Hermetic build
Cùng commit phải cho cùng output, lockfile pin dependency, Docker image tag cố định, compiler version cố định. “Chạy được trên máy tôi” biến mất khi build hermetic. Đây là prerequisite cho cache, cache chỉ đúng khi build deterministic.
Golden path
Một cách scaffold service mặc định: template repo với chuẩn lint, test framework, CI pipeline, monitoring setup. Dev mới tạo service theo golden path, tự do customize ở rìa (business logic) nhưng lõi (deploy, monitoring, logging) giống nhau.
Golden path giảm cognitive load, dev không phải quyết định “dùng test framework nào, CI setup thế nào” mỗi lần. Nhưng golden path cứng nhắc quá thì dev phải fight framework, cần balance giữa standardization và flexibility.
Platform vs product team
Khi nào cần platform team? Khi nhiều product team lặp lại cùng vấn đề: mỗi team tự viết CI pipeline, mỗi team tự setup monitoring, mỗi team tự build auth middleware. Platform team cung cấp template, tooling, baseline, product team focus vào domain.
Platform team cần SLO nội bộ: thời gian fix template bug, thời gian migrate khi đổi tool, quality tài liệu. Platform ship framework mà không có SLO → product team mất tin, tự build riêng → duplicate lại.
Product team giữ quyền quyết định domain. Platform không nên “cai trị” business logic, chỉ cung cấp infrastructure và convention.
Flake: phân loại và sửa đúng họ
Không phải mọi flake giống nhau. Phân loại theo nguyên nhân root cause:
Time-dependent: test dùng sleep(1000) hoặc assume response trong 2 giây. Fix: fake clock, deterministic wait (poll until condition, không sleep).
Shared state: test pass khi chạy riêng, fail khi chạy parallel vì share database hoặc Redis. Fix: isolate state per test, database per job, random prefix cho key.
Network: test gọi service ngoài (API, npm registry). Fix: stub hoặc contract test, không depend external service trong CI.
Random seed: test dùng random data không fix seed. Fix: seed cố định trong test, random chỉ cho exploration test riêng.
Label issue flake và track, platform team ưu tiên fix flake theo impact (frequency × pipeline delay). Flake nhiều nhất fix trước.
Local ≈ CI
Mục tiêu: make test hoặc pnpm test trên local chạy giống CI. Dev không bị surprise “CI đỏ nhưng local xanh”.
Không cần nhân bản toàn bộ production. Xác định vài invariant cần giống: schema migration chạy cùng lệnh, feature flag defaults staging phản ánh production đủ để không shock, seed data tối thiểu cho integration test (10-100 row đại diện, không clone full production vì PII và chi phí).
Docker Compose cho local integration test: database + cache + service dependencies. CI dùng cùng compose file hoặc equivalent. Dev chạy trước push, CI xác nhận, hai lần check cùng condition, giảm surprise khi merge.
Roadmap 90 ngày mẫu
Tháng 1, Đo và cứu CI: dashboard thời gian job + flake rate. Tắt test không cho giá trị hoặc tách chạy nightly. Bật remote cache nếu build chiếm >40% CI time.
Tháng 2, Local ≈ CI: make test khớp lệnh CI. Seed data + Docker Compose tối thiểu cho integration test. Fix top 5 flake.
Tháng 3, Deploy gần prod: staging pipeline rõ ràng, deploy một lệnh, rollback một lệnh. Runbook incident cho “ai revert khi nào”. Đo thời gian merge → staging.
Roadmap này adjust theo stack và team size, nhưng thứ tự “đo → local → deploy” thường hiệu quả cho hầu hết team.
Onboarding là DevEx
Engineer mới push PR đầu tiên ngày mấy? Ngày 1 → PR đầu tiên (dù chỉ docs fix) là healthy signal, môi trường setup dễ, tooling hoạt động. Tuần 2 vẫn chưa push được → bottleneck môi trường hoặc quyền truy cập.
Developer portal tối thiểu không cần Backstage, một trang nội bộ hoặc README gồm: cách chạy service trong 15 phút, cách chạy test + lint, ai on-call, link runbook deploy. Đây là 80/20 của “portal” trước khi self-host hệ meta phức tạp.
Secrets và DevEx
Secret là friction phổ biến nhất cho engineer mới. Template .env.example luôn cập nhật. Dùng secret manager hoặc direnv + local file gitignored có hướng dẫn rõ. CI dùng OIDC thay hardcode key nếu provider hỗ trợ.
Secret trên Slack thay vì secret manager là anti-pattern, mất audit trail, key rotate phải nhắn lại mọi người, engineer mới không biết hỏi ai.
Anti-pattern DevEx
Quá nhiều bước thủ công trước mỗi push, 5 lệnh copy từ wiki, mỗi lệnh có parameter phải sửa. Automate hoặc wrap trong Makefile.
CI không cache nhưng build full mỗi PR, đốt tiền cloud và kiên nhẫn dev. Bật cache là quick win.
Test suite không maintain, test 30 phút không ai biết test gì, fail random, pass cũng random. Tắt test không có giá trị, giữ suite lean.
Monorepo vs polyrepo
Monorepo: một PR cross-service dễ, refactor toàn codebase atomic. Nhưng CI phải chỉ build affected packages (dependency graph), không build toàn repo mỗi PR. Tooling (Nx, Turborepo, Bazel) cần đầu tư.
Polyrepo: CI đơn giản hơn từng repo. Nhưng thay đổi contract (API, shared types) cần release coordination, PR ở repo A phụ thuộc PR ở repo B, deploy thứ tự đúng.
Không có đáp án tuyệt đối, chỉ có chi phí chuyển đổi bạn chấp nhận. Team nhỏ thường bắt đầu monorepo (đơn giản hơn), scale lên polyrepo khi CI monorepo quá chậm.
Tóm tắt
DevEx là hệ thống feedback loop, edit → test → push → CI → review → merge → deploy. Bottleneck ở bất kỳ bước nào đều làm chậm toàn pipeline.
Đo trước khi đầu tư: CI time, flake rate, PR lead time, deploy time. Fix bottleneck lớn nhất trước, thường là CI chậm hoặc flake.
Flake là nợ lãi kép, phân loại (time, shared state, network, random) và fix có chủ đích. Remote cache build giảm CI time đáng kể. Local ≈ CI giảm surprise khi merge.
Platform team cần SLO nội bộ, ship framework không có SLO thì product team mất tin. Golden path chuẩn hóa lõi, cho flexibility ở rìa.
Onboarding là phần DevEx bị bỏ quên, đo “ngày đầu tiên push PR” là metric đơn giản mà nói lên nhiều điều. Portal tối thiểu (README + runbook) thường đủ trước khi invest Backstage.