- Clone repo TicTac, build và chơi được một ván trên Android
- Làm việc trên branch cá nhân với checkpoint Git, và viết rules file pin đúng stack cho AI pair
Clone TicTac, Git checkpoint và rules file cho AI
Clone repo TicTac, chạy nó trên Android, tạo branch cá nhân, và viết rules file pin đúng stack để AI không gợi ý code Android thuần.
Từ bài này trở đi bạn làm việc trên một app có thật: TicTac, game cờ ca-rô chạy trên Android và iOS, cùng một codebase Kotlin.
Bạn không gõ lại nó. Bạn mở nó ra, đọc đúng phần đang học, rồi sửa. Cách học đó gần với công việc thật hơn nhiều so với gõ lại từng dòng của một app đồ chơi.
Bài này lo ba việc để phần còn lại của khóa chạy trơn: lấy repo về, dựng lưới an toàn bằng Git, và đặt luật cho AI.
Lấy Repo Về Và Chạy Thử
Repo là private. Ngay khi đơn hàng hoàn tất, hệ thống tự gửi lời mời vào repo cho đúng tên tài khoản GitHub bạn nhập lúc mua khóa. Nhận lời mời bằng một trong hai cách:
- Mở email GitHub gửi tới (địa chỉ email của tài khoản GitHub đó) và bấm View invitation, hoặc
- Đăng nhập github.com bằng đúng tài khoản ấy, mở github.com/notifications hoặc vào thẳng
https://github.com/dan-tech-academy/kotlin-tictac-101/invitations, rồi bấm Accept invitation.
Chấp nhận xong là clone được:
git clone https://github.com/dan-tech-academy/kotlin-tictac-101.git
cd kotlin-tictac-101
Repo private nên lần clone đầu tiên Git sẽ hỏi đăng nhập GitHub. Đăng nhập bằng đúng tài khoản đã nhận lời mời. Nếu Git hỏi mật khẩu ngay trong terminal thì mật khẩu GitHub sẽ không được nhận; cách dễ nhất là cài GitHub CLI và chạy gh auth login một lần, rồi clone lại.
Chưa clone được? Lời mời có thể đang nằm trong hộp thư rác, hoặc chờ ở github.com/notifications. Kiểm hai chỗ đó trước, và kiểm bạn đang đăng nhập đúng tài khoản đã nhập lúc mua. Quá 30 phút sau khi mua mà vẫn chưa thấy, liên hệ hỗ trợ kèm tên tài khoản GitHub của bạn.
Mở thư mục vừa clone bằng Android Studio, đợi Gradle sync xong, rồi build:
./gradlew :androidApp:assembleDebug
Lần đầu mất vài phút. Kết thúc bằng BUILD SUCCESSFUL là máy bạn dựng được toàn bộ stack của khóa.
Cắm máy hoặc mở emulator, chọn run configuration chạy module androidApp trong IDE, nhấn Run. Chơi hết một ván: chọn cỡ bàn, chọn độ khó, đánh tới khi ra kết quả. Bạn vừa thấy đích đến của khóa học.
Có macOS thì mở thêm thư mục iosApp bằng Xcode và nhấn Run. Build phase của Xcode tự gọi Gradle để sinh framework từ code Kotlin chung. Không có macOS thì bỏ qua, mọi thứ còn lại y hệt.
Trước khi đọc code, mở file docs/reading-order.md trong repo. Nó xếp các file theo thứ tự từ dễ tới khó: bắt đầu từ những enum rất ngắn, kết thúc ở ui/AppHost.kt, và mỗi file chỉ dùng những khái niệm đã gặp ở các file trước. Mỗi bài của khóa sẽ chỉ đúng file cần mở. Khi muốn tự đọc thêm ngoài bài, cứ đi theo thứ tự trong file đó.
Branch Cá Nhân, Không Đụng master
Mọi thay đổi của bạn nằm trên branch riêng. master giữ nguyên làm bản gốc để đối chiếu khi bạn sửa hỏng thứ gì đó.
git switch -c hoc-tictac
Kiểm tra đang đứng đúng chỗ:
git branch --show-current
# hoc-tictac
Đây là branch local, chỉ nằm trên máy bạn, và nó ở đó suốt khóa học.
Quyền của bạn trên repo là chỉ đọc: clone được, kéo code mới về được, nhưng không đẩy lên được và không fork được. Nếu bạn thử git push, Git sẽ dừng lại. Có thể nó báo branch chưa có upstream trước; làm theo gợi ý của nó thì GitHub trả về lỗi kiểu:
remote: Permission to dan-tech-academy/kotlin-tictac-101.git denied to <tên-bạn>.
fatal: unable to access 'https://github.com/dan-tech-academy/kotlin-tictac-101.git/': The requested URL returned error: 403
Lỗi 403 này là bình thường, không phải bạn làm sai. Code của bạn không mất gì: mọi commit vẫn nằm nguyên trên máy bạn. Vì lịch sử chỉ sống ở máy, hãy commit thường xuyên; mỗi commit là một checkpoint bạn quay về được, như phần dưới sẽ dạy.
Bốn Lệnh Git Cho Checkpoint
Git có hàng trăm lệnh. Trong khóa này bạn cần đúng bốn.
git status # đang thay đổi những file nào
git add . # đánh dấu tất cả thay đổi để lưu
git commit -m "mô tả" # lưu lại thành một checkpoint
git log --oneline # xem danh sách checkpoint đã có
Tạo checkpoint đầu tiên ngay bây giờ, khi app còn chạy đúng:
git commit --allow-empty -m "chore: mốc xuất phát, app chạy được"
Từ giờ, cứ mỗi lần app chạy đúng như bạn muốn, chạy hai dòng:
git add .
git commit -m "feat: đổi màu ô đã đánh"
Một commit là một điểm quay về. Cứ 15-30 phút mà app còn chạy được thì commit. Lỗi thì sửa nhanh. Thứ ngốn thời gian là không biết nó xuất hiện từ lúc nào.
Khi mọi thứ hỏng
Đây là lệnh cứu mạng. Nó vứt bỏ mọi thay đổi chưa commit và đưa code về đúng checkpoint gần nhất:
git restore .
Muốn quay về xa hơn, xem danh sách rồi chọn:
git log --oneline
# a1b2c3d feat: đổi màu ô đã đánh
# e4f5a6b chore: mốc xuất phát, app chạy được
git restore --source e4f5a6b .
Cảnh báo:
git restore .xóa hẳn phần code chưa commit. Chắc chắn bạn không vứt đi thứ mình cần.
Repo đã có sẵn .gitignore đúng cho KMP, nên git status của bạn sẽ chỉ hiện những file bạn thật sự sửa. Nếu nó hiện build/, .gradle/ hay local.properties, nghĩa là có gì đó bị thêm nhầm. Khi đó chạy git rm -r --cached . rồi git add . để áp lại.
AI Pair: Đặt Luật Trước Khi Nhờ Vả
Bạn sẽ dùng AI trong khóa này để giải thích code, để đọc thông báo lỗi, để sinh phần code lặp đi lặp lại. Nhưng có một vấn đề rất cụ thể với KMP:
Phần lớn dữ liệu huấn luyện của AI là code Android thuần. Hỏi "viết cho tôi một ViewModel", nó rất dễ trả về import android.content.Context, androidx.compose.ui.platform.LocalContext, hoặc AndroidViewModel. Bản build Android vẫn xanh với những thứ đó, nên lỗi dễ lọt qua. Nhưng chúng làm đỏ bản biên dịch riêng của commonMain, cũng là bản mà iOS cần: ./gradlew :shared:compileCommonMainKotlinMetadata báo Unresolved reference 'android'.
Nó cũng hay gợi ý những thư viện phổ biến mà repo này cố tình không dùng. Gợi ý sai loại đó tốn của bạn cả buổi, vì code trông rất hợp lý cho tới lúc build đỏ.
Cách chữa: viết luật ra file, đặt ở gốc repo, để công cụ AI đọc trước mỗi lần trả lời.
Tạo file AGENTS.md (hoặc CLAUDE.md, tùy công cụ bạn dùng) ở thư mục gốc:
# Luật của project
## Ngữ cảnh
Project Kotlin Multiplatform dùng Compose Multiplatform, chạy trên Android và iOS.
KHÔNG phải project Android thuần. Module chứa code là `:shared`.
Package gốc: `com.dantech.academy.tictac`.
## Phiên bản đã pin, không đề xuất bản khác
- Kotlin 2.4.10
- Compose Multiplatform 1.11.1
- Koin 4.2.2
- androidx-lifecycle 2.10.0
- material3 1.9.0
- multiplatform-settings 1.3.0
- kotlinx-serialization 1.11.0
- kotlinx-collections-immutable 0.5.2
## Cấm dùng
- Room, SQLDelight, hay bất kỳ SQL nào. Project lưu dữ liệu bằng key-value.
- Navigation 3 hay bất kỳ thư viện điều hướng nào. Project tự quản màn hình.
- `androidContext()` của Koin. Không có Android Context trong `commonMain`.
- `android.*` trong `commonMain`.
## Bắt buộc
- Mặc định viết vào `shared/src/commonMain/kotlin/`.
- Giao diện: Compose Multiplatform + Material 3.
- Kiến trúc: MVVM. Model không biết ViewModel, ViewModel không biết Composable.
- Bất đồng bộ: coroutines và Flow. Không dùng callback.
## Khi trả lời
- Chỉ ra rõ đoạn code thuộc source set nào.
- Nếu buộc phải viết code riêng cho từng nền tảng, dùng expect/actual và nói rõ vì sao.
- Giải thích ngắn gọn trước khi đưa code, không viết lại cả file nếu chỉ đổi vài dòng.
Commit file này. Nó là một phần của project.
Ba câu hỏi tốt và một câu hỏi tệ
Câu hỏi tệ:
"Viết game cờ ca-rô cho tôi."
Bạn nhận về vài trăm dòng không đọc nổi, không biết sai chỗ nào khi nó không chạy.
Ba câu hỏi tốt:
"Giải thích dòng
var count by remember { mutableStateOf(0) }này, từng phần một."
"Đoạn code này báo lỗi
Unresolved reference 'Context'trong commonMain. Vì sao và sửa thế nào theo hướng multiplatform?"
"Đọc file
shared/src/commonMain/kotlin/com/dantech/academy/tictac/game/Mark.ktvà giải thích enum này dùng để làm gì."
Điểm chung: nhỏ, cụ thể, và bạn kiểm tra được kết quả. Nguyên tắc suốt khóa: không commit code mà bạn không giải thích được. Không hiểu thì hỏi tiếp cho đến khi hiểu, hoặc bỏ đoạn đó đi.
Cập Nhật Code Khi Khóa Học Đổi
Thỉnh thoảng repo và bài học được cập nhật cùng lúc: code trích trong bài đổi theo code mới. Khi nhận thông báo có bản cập nhật, làm ba bước dưới đây trong thư mục kotlin-tictac-101.
1. Lưu phần đang làm trên branch cá nhân của bạn:
git add -A
git commit -m "bai lam cua toi"
git add -A gom mọi thay đổi trong cả repo, kể cả file bạn đã xóa. Nếu Git báo nothing to commit thì nghĩa là bạn đã lưu hết rồi, đi tiếp.
2. Về master và kéo code mới:
git checkout master
git pull
Bạn chưa từng sửa gì trên master, nên git pull chỉ việc đưa nó lên bản mới nhất, không có conflict.
Sau đó mở lại project trong Android Studio và bấm Sync Project with Gradle Files (hoặc chạy ./gradlew build một lần). Bản mới có thể đổi phiên bản thư viện, nếu chưa sync thì IDE có thể báo đỏ dù code vẫn đúng.
3. Tạo branch mới từ master để học tiếp:
git checkout -b hoc-tiep
git checkout -b làm đúng việc của git switch -c ở trên: tạo branch mới và đứng luôn trên nó. Lần cập nhật sau, đặt tên khác (hoc-tiep-2...) vì tên cũ đã có.
Branch cũ (hoc-tictac) vẫn nằm trên máy bạn với đủ các commit, xem lại lúc nào cũng được bằng git log --oneline hoc-tictac. Muốn mang file AGENTS.md sang branch mới thì chạy git checkout hoc-tictac -- AGENTS.md rồi commit.
Đừng học tiếp trên branch cũ sau khi khóa học đã cập nhật: bài trên web đã theo code mới, nên code trong bài sẽ không còn khớp với code trên máy bạn.
Commit là điểm quay về. Commit sớm, commit thường xuyên, vì lịch sử đó chỉ nằm trên máy bạn.