[{"content":"","date":"12 ဇူလိုင် 2026","externalUrl":null,"permalink":"/tags/database/","section":"Tags","summary":"","title":"Database","type":"tags"},{"content":"","date":"12 ဇူလိုင် 2026","externalUrl":null,"permalink":"/tags/go/","section":"Tags","summary":"","title":"Go","type":"tags"},{"content":"","date":"12 ဇူလိုင် 2026","externalUrl":null,"permalink":"/tags/gorm/","section":"Tags","summary":"","title":"Gorm","type":"tags"},{"content":"","date":"12 ဇူလိုင် 2026","externalUrl":null,"permalink":"/tags/postgresql/","section":"Tags","summary":"","title":"Postgresql","type":"tags"},{"content":"","date":"12 ဇူလိုင် 2026","externalUrl":null,"permalink":"/posts/","section":"Posts","summary":"","title":"Posts","type":"posts"},{"content":"","date":"12 ဇူလိုင် 2026","externalUrl":null,"permalink":"/tags/sqlc/","section":"Tags","summary":"","title":"Sqlc","type":"tags"},{"content":"","date":"12 ဇူလိုင် 2026","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"12 ဇူလိုင် 2026","externalUrl":null,"permalink":"/","section":"Thoughts","summary":"","title":"Thoughts","type":"page"},{"content":"Read this article in English\nနိဒါန်း # ဒီရက်ပိုင်း image-server ဆိုတဲ့ image upload နဲ့ transformation server တခုကို Go နဲ့ ရေးနေတာရှိပါတယ်။ GORM လို ORM ကို သုံးမလား၊ ဒါမှမဟုတ် sqlc လို code generator ကို သုံးမလား ဆိုပြီး ရွေး ချယ်စရာ ၂ ခုရှိလာတာမှာ ကျွန်တော်ကတော့ sqlc ကို ရွေးဖြစ်လိုက်ပါတယ်။\nဒီ post မှာတော့ sqlc ဆိုတာ ဘာလဲ၊ ဘယ်လိုအလုပ်လုပ်လဲ၊ အားနည်းချက် အားသာချက်တွေက ဘာတွေလဲ၊ GORM နဲ့ ဘာကွာလဲ၊ ပြီးတော့ git hooks တွေအပါအဝင် ကျွန်တော့ရဲ့ project ထဲမှာ ဘယ်လို configure လုပ်ထားလဲ ဆိုတာတွေကို sharing လုပ်ချင်ပါတယ်။\nsqlc ဆိုတာ ဘာလဲ? # sqlc က ORM မဟုတ်ပါဘူး။ sqlc က ရိုးရိုး SQL file တွေကနေ type-safe ဖြစ်တဲ့ Go code ကို generate လုပ်ပေးတာပါ။ Query builder လည်း မရှိသလို runtime မှာမှ ပေါ်လာတဲ့ magic code တွေလည်း မရှိပါဘူး။ အားလုံးက build time မှာပဲ generate လုပ်သွားတာပါ။\nဒါက ကျွန်တော့် project ထဲက SQL query file တခုပါ။\n-- name: GetImage :one SELECT * FROM images WHERE id = $1; -- name: CreateImage :one INSERT INTO images ( original_filename, content_hash, mime_type, width, height, size_bytes, storage_key ) VALUES ( $1, $2, $3, $4, $5, $6, $7 ) RETURNING *; sqlc generate command ကို run လိုက်တာနဲ့ အောက်ကလို Go function တခုကို generate လုပ်ပေးသွားပါလိမ့်မယ်။\nfunc (q *Queries) GetImage(ctx context.Context, id uuid.UUID) (Image, error) Image struct ကိုလည်း column တိုင်းအတွက် Go type တွေ (uuid.UUID, int32, int64) နဲ့ generate လုပ်ပေးပါတယ်။ ကိုယ်တိုင် လုံးဝရေးစရာမလိုပါဘူး။\nsqlc က Table Structure ကို ဘယ်ကရမှာလဲ? # sqlc.yaml file ထဲမှာ ကိုယ့်ရဲ့ database migration file တွေရှိတဲ့ နေရာကို ညွှန်ပေးလိုက်ရုံပါပဲ။\nversion: \u0026#34;2\u0026#34; sql: - engine: \u0026#34;postgresql\u0026#34; queries: \u0026#34;internal/db/queries\u0026#34; schema: \u0026#34;migrations\u0026#34; gen: go: package: \u0026#34;db\u0026#34; out: \u0026#34;internal/db\u0026#34; sql_package: \u0026#34;pgx/v5\u0026#34; migrations folder ထဲက SQL file တွေအားလုံးကို ဖတ်၊ .down.sql ဆိုတဲ့ rollback file တွေကို ကျော်ပြီး .up.sql file တွေကိုပဲ ယူ၊ database အသစ်မှာ run သလို အစဉ်လိုက် replay ပြန်လုပ်ပြီး schema ကို memory ထဲမှာ ဆောက်ပါတယ်။ ပြီးမှ query တိုင်းကို အဲ့ဒီ schema နဲ့ တိုက်စစ်တာပါ။\nဒါကြောင့် migration ထဲမှာ column နာမည်ပြောင်းပြီး query ကို ပြင်ဖို့ မေ့သွားခဲ့တာမျိုးဆိုရင် code generation က fail ဖြစ်ပါလိမ့်မယ်။ Production ကျမှ မဟုတ်ဘဲ build time မှာတင် သိရမှာပါ။\nsqlc ရဲ့ အားသာချက်တွေအနေနဲ့ # SQL ပဲ ရေးဖို့လိုပါတော့မယ်။ DSL အသစ် တခုကို ထပ်လေ့လာနေစရာမလိုတော့ပါဘူး။ Compile-time safety ဖြစ်မယ်။ Data type မှားတာ၊ column နာမည်မှားတာ၊ parameter အရေအတွက် မှားတာတွေကို compile time မှာတင် သိပါမယ်။ Hidden query တွေ မရှိတော့ပါဘူး။ Database မှာ run သွားတဲ့ SQL က ကိုယ်ရေးထားခဲ့တဲ့ SQL အတိုင်း အတိအကျပါပဲ။ မမျှော်မှန်းထားတဲ့ N+1 query တွေ မရှိနိုင်တော့ပါဘူး။ Runtime overhead မရှိတော့ပါဘူး။ Generated code က QueryRow နဲ့ Scan ပဲ ပါပါတော့မယ်။ No reflection ပါ။ SQL injection မရှိတော့ပါဘူး။ Query တိုင်းက parameterized ($1, $2) ဖြစ်နေတဲ့အတွက် string concatenation မရှိနိုင်တော့ပါဘူး။ sqlc ရဲ့ အားနည်းချက်တွေ # SQL ကို ကိုယ်တိုင် ရေးရမယ်။ SQL နဲ့ လုံးဝမရင်းနီးပဲ ORM သုံးနေကျ team အတွက်ဆိုရင်တော့ အဆင်မပြေပါဘူး။ Dynamic query တွေအတွက် အဆင်မပြေနိုင်ပါဘူး။ Query တွေက static string တွေ ဖြစ်တဲ့အတွက် optional filter ငါးခုလောက်ပါတဲ့ search endpoint မျိုး ဆောက်ရရင် .Where() တွေ ဆက်သုံးလို့ရတဲ့ GORM နဲ့ယှဉ်ဖို့ မဖြစ်နိုင်ပါဘူး။ Relation တွေ၊ eager loading တွေ သုံးလို့မရပါဘူး။ Join လုပ်ထားတဲ့ data လိုချင်ရင် JOIN statement ကို ကိုယ်တိုင် ရေးဖို့လိုပါမယ်။ Build step တခု ပိုလာပါမယ်။ Query ပြောင်းတိုင်း generate command ပြန် run ရပြီးတော့ ထွက်လာတဲ့ generated code ကိုပါ commit လုပ်ရပါမယ်။ (ဒါကို git hooks နဲ့ ဖြေရှင်းလို့ရပါတယ်။ နောက်မှာဆက်ရှင်းပြထားပါတယ်။) ကိုယ်တိုင် migrate လုပ်ရပါမယ်။ sqlc က schema ပြောင်းတာတွေအတွက် GORM ရဲ့ AutoMigrate() လိုမျိုး support မပေးထားပါဘူး။ GORM နဲ့ ယှဉ်ကြည့်လို့ရအောင် table လေး လုပ်ပေးထားပါတယ် # sqlc GORM What you write Plain SQL Go structs + method chains Type safety Compile time Runtime (reflection) Query visibility Exactly what you wrote Built for you behind the scenes Performance No reflection, no overhead Reflection + possible N+1 queries Dynamic queries Clunky Easy Learning curve Know SQL, tiny Go API Learn GORM\u0026rsquo;s conventions Migrations Separate tool AutoMigrate (risky for production) Relation တွေများပြီး dynamic filtering အများကြီးလိုတဲ့ project လိုမျိုးမှာ GORM က ပိုအဆင်ပြေပါတယ်။ Query တွေကို အတိအကျရေးချင်ပြီး performance ကို ကြိုတွက်ထားချင်တယ်ဆိုရင်တော့ sqlc က ပိုသင့်တော်ပါတယ်။\nImage Server အတွက် sqlc ကို ဘာလို့ရွေးခဲ့တာလဲ? # ကျွန်တော့် image-server project မှာ non-negotiable security rule တခု ရှိပါတယ်။ အဲ့ဒါက parameterized queries only ပါ။ sqlc ကို သုံးလိုက်တာနဲ့ string concatenation နဲ့ query ဆောက်လို့ လုံးဝမရတော့တဲ့အတွက် SQL injection ဖြစ်လာစရာမရှိတော့ပါဘူး။\nနောက်တခုက ကျွန်တော့် query တွေက ရိုးရိုး CRUD တွေပဲ ဖြစ်နေတာလည်း ပါပါတယ်။ Image record အသစ်ဆောက်တာ၊ ID နဲ့ ပြန်ရှာတာ၊ deduplication အတွက် content hash နဲ့ ရှာတာ၊ ဖျက်တာတွေပါပဲ။ Dynamic filtering လုံးဝမရှိတဲ့အတွက် sqlc ရဲ့ အားနည်းချက်ထက် သူ့ရဲ့ အကြီးမားဆုံးအားသာချက် (compile-time checking နဲ့ exact SQL) ရနေတာပါ။\nGenerated Code တွေ Sync ဖြစ်နေအောင် Git Hooks နဲ့ ဘယ်လိုထိန်းထားလဲ? # Generated code တွေမှာ အဖြစ်များတဲ့ပြဿနာတခု ရှိပါတယ်။ တယောက်ယောက်က query ကို ပြင်ပြီး generate command ပြန် run ဖို့ မေ့သွားရင် generated code အဟောင်းက commit ထဲ ပါသွားတတ်ပါတယ်။ အဲ့ဒီပြဿနာကို .githooks/ folder ထဲမှာ git hooks တွေနဲ့ ပိတ်ထားပါတယ်။ Project ကို clone လုပ်ပြီးရင် အောက်က command ကို terminal မှာ တခါ run ပေးဖို့လိုပါမယ်။ တခါပဲ လိုတာပါ။\ngit config core.hooksPath .githooks 1. pre-commit: drift check # pre-commit hook က generator ကို ပြန် run ပြီး ထွက်လာတဲ့ generated code က staged code နဲ့ မတူရင် commit ကို fail လိုက်ပါလိမ့်မယ်။\nif [[ -f sqlc.yaml ]]; then make -s sqlc-gen \u0026gt; /dev/null if ! git diff --exit-code -- internal/db \u0026gt; /dev/null 2\u0026gt;\u0026amp;1; then echo \u0026#34;❌ internal/db is out of sync with the SQL queries.\u0026#34; echo \u0026#34; Run \u0026#39;make sqlc-gen\u0026#39;, stage the result, and recommit.\u0026#34; exit 1 fi fi ဒါကြောင့် query ကိုပြင်ထားပြီး updated generated code မပါဘဲ commit လုပ်လို့ လုံးဝမရပါဘူး။ OpenAPI ကနေ generate လုပ်ထားတဲ့ code အတွက်လည်း ဒီ hook ထဲမှာ အတူတူပဲစစ်ထားပါတယ်။\n2. pre-push: quality gates # pre-push hook ကတော့ ပိုကြီးတဲ့ gate တွေကို push မလုပ်ခင် အရင် run ပါတယ်။ golangci-lint၊ 90% test-coverage gate၊ endpoint တိုင်းကို OpenAPI spec နဲ့ တိုက်စစ်ထားတဲ့ API test တွေ၊ ပြီးတော့ Docker container နဲ့ run တဲ့ full-stack e2e test တွေပါပါတယ်။ ပထမ gate သုံးခုကို CI ကလည်း repo ဘက်မှာ ပြန် run တဲ့အတွက် --no-verify option နဲ့ဆိုရင်တောင် bad code တွေ main branch ပေါ် ရောက်မသွားနိုင်တော့ပါဘူး။\nရလဒ်ကတော့ schema၊ query တွေနဲ့ Go type တွေက အမြဲတမ်း sync ဖြစ်နေမှာပါ။\nနိဂုံး # ကိုယ့်ရဲ့ project မှာက complex relation တွေနဲ့ dynamic query တွေ အများကြီး ရှိတယ်ဆိုရင် GORM ကိုသာရွေးပါ။ ဒါပေမယ့် SQL ရေးရတာ ပိုအဆင်ပြေမယ်၊ compile-time safety ဖြစ်မယ်၊ ကြိုတွက်ထားလို့ရတဲ့ query performance လိုချင်တယ်၊ database ပေါ်မှာ ဘာ SQL run မလဲဆိုတာ အတိအကျကြိုရေးထားချင်တယ်ဆိုရင်တော့ sqlc ကို စမ်းသုံးကြည့်ဖို့ recommend လုပ်ချင်ပါတယ်။\n","date":"12 ဇူလိုင် 2026","externalUrl":null,"permalink":"/posts/sqlc-go/","section":"Posts","summary":"","title":"ဘာလို့ GORM အစား sqlc လဲ","type":"posts"},{"content":"","date":"24 မတ် 2026","externalUrl":null,"permalink":"/tags/claude/","section":"Tags","summary":"","title":"Claude","type":"tags"},{"content":"","date":"24 မတ် 2026","externalUrl":null,"permalink":"/tags/claude-code/","section":"Tags","summary":"","title":"Claude Code","type":"tags"},{"content":"Read this article in English\nနိဒါန်း # အခုလက်ရှိက Claude Code ရော Antigravity ရောကို အလုပ်အတွက်ရယ် personal အတွက်ရယ်နဲ့ သုံးဖြစ်နေတယ်။ အဲ့မှာ တခုပြဿနာရှိလာတာက session ထဲက conversation တွေကို တခြားတယောက်ကို share ချင်တာမျိုးဆိုရင် အစဉ်မပြေတာပါပဲ။ Log အရှည်ကြီးတွေ ပို့ရတာ ဒါမှမဟုတ် screen record ဖမ်းပြီး ပို့ရတာမျိုးက သိပ်အဆင်မပြေပါဘူး။\nအဲဒါနဲ့ ရှာရင်းဖွေရင်း claude-replay ဆိုတဲ့ tool တခုသွားတွေ့တယ်။ claude-replay က AI နဲ့ chat session တွေကို video လိုကြည့်လို့ရအောင် interactive playback HTML file ထုတ်ပေးတာပါ။\nလက်ရှိတော့ Claude Code၊ Cursor၊ နဲ့ Codex CLI တွေကိုပဲ လုပ်ပါသေးတယ်။\nအသုံးဝင်တဲ့ command တွေ # အသုံးဝင်တဲ့ command တွေအနေနဲ့ဆိုရင်:\nclaude-replay: မှာ လက်ရှိ တွေကို ကြည့်လို့ရမယ် claude-replay abc123def456 -o replay.html: ပေးပြီး Interactive playback HTML file ထုတ်လို့ရမယ် claude-replay session.jsonl --no-thinking --no-tool-calls -o replay.html: တွေ မလိုချင်ရင် ဖြုတ်ခဲ့လို့ရမယ် claude-replay session.jsonl --turns 5-15 --speed 2.0 -o replay.html: လိုချင်တဲ့ အပိုင်းလေးကိုပဲ အမျိုးမျိုးနဲ့ ပြလို့ရမယ် claude-replay session.jsonl --theme dracula -o replay.html: လည်း ပြောင်းလို့ရပါမယ် နိဂုံး # Secret redaction နဲ့ တခြားအသုံးဝင်တာတွေရယ် single HTML file ဖြစ်တဲ့အတွက် တခြား page တခုခုမှာ embed လုပ်တာတွေရယ်ကို claude-replay GitHub Repo မှာကြည့်ပြီး စမ်းကြည့်စေချင်ပါတယ်။\n","date":"24 မတ် 2026","externalUrl":null,"permalink":"/posts/claude-replay-for-claude-code/","section":"Posts","summary":"","title":"Claude Code အသုံးပြုသူများအတွက် အသုံးဝင်မယ့် claude-replay အကြောင်း","type":"posts"},{"content":"","date":"24 မတ် 2026","externalUrl":null,"permalink":"/tags/claude-replay/","section":"Tags","summary":"","title":"Claude-Replay","type":"tags"},{"content":"","date":"24 မတ် 2026","externalUrl":null,"permalink":"/tags/tools/","section":"Tags","summary":"","title":"Tools","type":"tags"},{"content":"","date":"12 မတ် 2026","externalUrl":null,"permalink":"/tags/git/","section":"Tags","summary":"","title":"Git","type":"tags"},{"content":"Read this article in English\nနိဒါန်း # Git သုံးတဲ့အခါမှာ ကိုယ်က git commit လုပ်လိုက်ပြီးမှ ဖိုင်တချို့ ပါဖို့ကျန်ခဲ့တာပဲဖြစ်ဖြစ်၊ commit message မှားသွားတာပဲဖြစ်ဖြစ် ကြုံဖူးကြမှာပါ။ အဲ့လိုအချိန်မှာ commit လုပ်ပြီးသား file တွေကို ပြန်ပြင်ချင်ရင် git reset --soft ကို သုံးပြီး အလွယ်တကူ ပြန်ပြင်လို့ရပါတယ်။\nဒီ post မှာ git reset --soft HEAD^ နဲ့ git reset --soft HEAD~n ကို သုံးပြီး နောက်ဆုံး commit နဲ့ အရင်လုပ်ခဲ့တဲ့ commit တွေကို ဘယ်လို ပြန်ပြင်မလဲ ဆိုတာကို sharing လုပ်ချင်ပါတယ်။\nနောက်ဆုံး Commit ကို ပြန်ပြင်မယ်ဆိုရင်? # ကိုယ့်ရဲ့ နောက်ဆုံးလုပ်ခဲ့တဲ့ commit ကို ဖျက်ပြီး file တွေကို staging area ထဲ ပြန်ထားချင်တယ်ဆိုရင် အောက်က command ကို သုံးနိုင်ပါတယ်။\ngit reset --soft HEAD^ ဒီ command က HEAD (လက်ရှိ ရောက်နေတဲ့ နေရာ) ရဲ့ အရှေ့က တခုကို ပြန်ဆုတ်လိုက်တာပါ။ --soft ကို သုံးထားတဲ့အတွက် file တွေက မပျက်သွားပဲ staging area (index) မှာပဲ ကျန်နေခဲ့ပါမယ်။ အဲ့ဒီအခါမှာ ကိုယ်လိုချင်တဲ့ file တွေကို ထပ်ထည့်တာ၊ ဖြုတ်တာတွေ လုပ်ပြီးရင် အသစ်ပြန်ပြီး commit လုပ်လို့ရပါပြီ။\nအရင်က Commit တွေကို ပြန်ပြင်မယ်ဆိုရင်? # တကယ်လို့ နောက်ဆုံးတခုတည်း မဟုတ်ဘဲ နောက်ဆုံး နှစ်ခု၊ သို့မဟုတ် သုံးခုလောက်ကို ပြန်ပြင်ချင်တယ်ဆိုရင် HEAD~n ကို သုံးနိုင်ပါတယ်။\nဥပမာ နောက်ဆုံးလုပ်ခဲ့တဲ့ commit သုံးခုကို ပြန်ဖြုတ်ပြီး အစကနေ ပြန်ပေါင်းပြီး commit အသစ်တခုအနေနဲ့ လုပ်ချင်တယ်ဆိုရင်:\ngit reset --soft HEAD~3 ဒီ command က နောက်ဆုံး commit သုံးခုကို ဖျက်ပေးပြီး file တွေကို staging area ထဲ ပြန်ထည့်ပေးပါလိမ့်မယ်။ အဲ့ဒီနောက်မှာ အားလုံးကိုစုပြီး commit အသစ်တခုတည်း အနေနဲ့ ပြန်လုပ်နိုင်ပါတယ်။\nအခြား အသုံးဝင်တဲ့ Tips တွေ # 1. git reset --mixed နဲ့ ဘာကွာလဲ? # အကယ်၍ --soft အစား ဘာမှမထည့်ဘဲ git reset HEAD^ ဒါမှမဟုတ် git reset --mixed HEAD^ လို့ သုံးလိုက်ရင် file တွေက staging area (index) ကနေပါ ထွက်သွားပြီး modified state (unstaged) မှာ ရောက်သွားပါမယ်။ ပြန်ပြီး git add လုပ်ဖို့ လိုအပ်ပါတယ်။\n2. git reset --hard ကို သတိထားပါ # တကယ်လို့ git reset --hard HEAD^ လို့ သုံးလိုက်မယ်ဆိုရင်တော့ commit ကို ဖျက်ရုံတင်မကဘဲ file တွေထဲမှာ ပြင်ထားတဲ့ code တွေပါ အကုန်ပျက်သွားမှာ ဖြစ်ပါတယ်။ ဒါကြောင့် code တွေကို မဖျက်ဘဲ commit ပဲ ပြန်ပြင်ချင်တဲ့အခါ --soft ကို သုံးဖို့ သေချာဂရုစိုက်ပါ။\n3. Remote Server ပေါ်ကို ပြန်တင်မယ်ဆိုရင်? # ကိုယ့်ရဲ့ origin/remote ကို git push လုပ်ပြီးသား commit တွေကို reset လုပ်ပြီး ပြန်ပြင်ထားတယ်ဆိုရင် ရိုးရိုး git push လို့ မရတော့ပါဘူး။ History တွေ ပြောင်းသွားတဲ့အတွက် force push လုပ်ပေးဖို့ လိုအပ်ပါတယ်။\ngit push --force-with-lease origin main --force ထက် --force-with-lease ကို သုံးတာက တခြားသူတွေရဲ့ code တွေကို မတော်တဆ ဖျက်မိတာမျိုး မဖြစ်အောင် ကာကွယ်ပေးနိုင်ပါတယ်။\nနိဂုံး # git reset --soft ဟာ file တွေ မပျက်စေဘဲ commit history ကို ရှင်းလင်းအောင် ပြန်စီစဉ်တဲ့နေရာ၊ မတော်တဆ မှားသွားတဲ့ commit တွေကို ပြန်ပြင်တဲ့နေရာမှာ အလွန်အသုံးဝင်တဲ့ command တခုပါ။ ဒီ tip လေးတွေက နေ့စဉ် code ရေးတဲ့နေရာမှာ အထောက်အကူဖြစ်မယ်လို့ မျှော်လင့်ပါတယ်။\n","date":"12 မတ် 2026","externalUrl":null,"permalink":"/posts/git-reset-soft-head-tips/","section":"Posts","summary":"","title":"Git Commit တွေကို ဘယ်လို ပြန်ပြင်မလဲ (git reset --soft)","type":"posts"},{"content":"","date":"12 မတ် 2026","externalUrl":null,"permalink":"/tags/git-reset/","section":"Tags","summary":"","title":"Git Reset","type":"tags"},{"content":"","date":"12 မတ် 2026","externalUrl":null,"permalink":"/tags/github/","section":"Tags","summary":"","title":"Github","type":"tags"},{"content":"","date":"12 မတ် 2026","externalUrl":null,"permalink":"/tags/tips/","section":"Tags","summary":"","title":"Tips","type":"tags"},{"content":"","date":"9 မတ် 2026","externalUrl":null,"permalink":"/tags/filter-repo/","section":"Tags","summary":"","title":"Filter-Repo","type":"tags"},{"content":"Read this article in English\nနိဒါန်း # GitHub သုံးတဲ့သူတိုင်း ကိုယ့်ရဲ့ profile မှာ စိမ်းနေတဲ့ contribution graph လေးတွေကို မြင်ချင်မှာပါ။ ဒါပေမယ့် တခါတလေမှာ ကိုယ်က နေ့တိုင်း code တွေ ရေးပြီး push လုပ်နေပေမယ့် GitHub contribution မှာ သွားမပေါ်တာမျိုး ကြုံရနိုင်ပါတယ်။ အဓိကအကြောင်းအရင်းကတော့ ကိုယ့်ရဲ့ local Git config မှာ သုံးထားတဲ့ email နဲ့ GitHub အကောင့်မှာရှိတဲ့ email မတူလို့ပါ။\nဒီ post မှာတော့ ဘာကြောင့်အဲ့လိုဖြစ်ရလဲဆိုတာကို ပြန်ကြည့်ပြီး gitmailmap နဲ့ git filter-repo command တွေ သုံးပြီး အရင်ကလုပ်ခဲ့တဲ့ past commits တွေကိုပါ ဘယ်လိုပြန်ပြင်မလဲဆိုတာကို sharing လုပ်ချင်ပါတယ်။\nCommit တွေ ဘာလို့မပေါ်တာလဲ? # 1. Local နဲ့ GitHub ပေါ်က Email တွေကို စစ်ပါ # ပထမဆုံးအနေနဲ့ ကိုယ့်ရဲ့ commit တွေ ဘာလို့ မပေါ်လဲဆိုတာကို စစ်ကြည့်ရအောင်။\nTerminal မှာ အောက်က command ကို run ပြီး လက်ရှိ local git မှာ setup လုပ်ထားတဲ့ email ကို စစ်ကြည့်ပါ။\ngit config user.email အဲ့ဒီမှာ ပေါ်လာတဲ့ email က GitHub ပေါ်က Settings -\u0026gt; Emails ထဲမှာ ထည့်ထားတဲ့ email နဲ့ တူရပါမယ်။ အဲ့ email က “Verified” လည်း ဖြစ်နေဖို့ လိုပါတယ်။\n2. Commits တွေ GitHub ပေါ်ရောက်မရောက် စစ်ပါ # Local မှာပဲ commit လုပ်ပြီး git push မလုပ်ရသေးဘူးဆိုရင်လည်း contribution မှာ မပေါ်ပါဘူး။ နောက်ပြီး မလိုအပ်တဲ့ branch တွေမှာ commit လုပ်ထားပြီး default branch (များသောအားဖြင့် main သို့မဟုတ် master) ကို merge မလုပ်ရသေးရင်လည်း မပေါ်တတ်ပါဘူး။ ပြီးတော့ private rep တွေမှာ contribution တွေကို profile မှာ မပေါ်အောင် ပိတ်ထားတာမျိုးလည်း ဖြစ်နိုင်ပါတယ်။\nEmail တွေ မတူခဲ့ဘူးဆိုရင်? # ပြဿနာက email မတူတာဆိုတာ သေချာသွားပြီဆိုရင်တော့ နောက်ပိုင်းထပ်လာမဲ့ commit တွေအတွက် email အမှန်ကို အရင်ပြောင်းပေးရပါမယ်။\ngit config --global user.name \u0026#34;Your Name\u0026#34; git config --global user.email \u0026#34;your-real-email@example.com\u0026#34; ဒီလိုပြောင်းလိုက်တာနဲ့ နောက်ထပ် အသစ်လုပ်မယ့် commit တွေအားလုံးက GitHub မှာ အလိုအလျောက် contribution အနေနဲ့ ပေါ်လာပါလိမ့်မယ်။ လက်ရှိ project တခုတည်းအတွက်ပဲ သီးသန့်ထားချင်ရင်တော့ --global မပါဘဲ သုံးပေးပါ။\nအရင်ကလုပ်ခဲ့တဲ့ Commits တွေကို ဘယ်လိုပြင်မလဲ? # ပြဿနာက အရင်က email မပြောင်းထားခင်က လုပ်ခဲ့တဲ့ commit အဟောင်းတွေပါ။ သူတို့ထဲမှာ email အမှားတွေက ပါသွားပြီးပါပြီ။ အဲ့ဒါတွေကို date-time တွေ မပြောင်းစေပဲ email နဲ့ name အမှန်ပြန်ပြောင်းချင်တယ်ဆိုရင် .mailmap နဲ့ git filter-repo ကို တွဲသုံးဖို့ လိုပါတယ်။\n1. git filter-repo ကို Install လုပ်ပါ # git filter-repo က Git ရဲ့ built-in filter-branch ထက် ပိုမြန်ပြီး ပို safe ဖြစ်တဲ့ tool တခုပါ။\nmacOS မှာဆိုရင် brew install git-filter-repo Linux သို့မဟုတ် Windows မှာဆိုရင် pip install git-filter-repo နဲ့ install လုပ်နိုင်ပါတယ်။ ဘာမှမလုပ်ခင် ကိုယ့်ရဲ့ repository ကို local မှာ backup ယူထားဖို့ ပေးချင်ပါတယ်။\n2. .mailmap File လုပ်ပါ # ကိုယ့် project ရဲ့ root directory မှာ .mailmap ဆိုတဲ့ file တခုဆောက်ပါ။ အဲ့ဒီ file ထဲမှာ အောက်က format အတိုင်း ထည့်ပေးပါ။\nKyaw Kyaw Myo \u0026lt;your-real-email@example.com\u0026gt; \u0026lt;user@github.com\u0026gt; ဒီနေရာမှာ:\nKyaw Kyaw Myo နေရာမှာ မကိုယ့်ရဲ့ နာမည်အမှန် your-real-email@example.com နေရာမှာ GitHub မှာ verified ဖြစ်နေတဲ့ email အမှန် user@github.com နေရာမှာ အရင်က သုံးခဲ့မိတဲ့ မှားနေတဲ့ email ကို ထည့်ပါ။ 3. History ကို Rewrite လုပ်ပါ # .mailmap file ကို သိမ်းပြီးပြီဆိုရင် အောက်က command ကို run ပါ။\ngit filter-repo --mailmap .mailmap ဒီ command က commit history တွေကို လိုက်ဖတ်ပြီး .mailmap ပါ အချက်အလက်တွေအတိုင်း အရင်က မှားခဲ့တဲ့ email တွေကို အမှန်ပြန်ပြောင်းပေးသွားမှာပါ။ မူလ commit date တွေလည်း ပြောင်းသွားမှာ မဟုတ်ပါဘူး။\nForce Push # Commit history တွေကို rewrite လုပ်လိုက်တာဖြစ်တဲ့အတွက် ရိုးရိုး git push နဲ့ တင်လို့ မရတော့ပါဘူး။ Force push လုပ်ပေးဖို့ လိုပါတယ်။\ngit push --force-with-lease --all origin git push --force-with-lease --tags origin ဒါဟာ repo ရဲ့ history ကို rewrite လိုက်တာမလို့ ကိုယ်တယောက်တည်း သုံးတဲ့ personal repo တွေမှာပဲ လုပ်သင့်ပါတယ်။ တကယ်လို့ အဖွဲ့လိုက်လုပ်နေတဲ့ project ဆိုရင်တော့ project ကို clone ထားတဲ့ တခြား team member တွေပါ ရိုက်ခတ်မှုရှိနိုင်လို့ သတိထားပါ။\nနိဂုံး # Force push လုပ်ပြီး ခဏလောက်စောင့်လိုက်တာနဲ့ မိမိရဲ့ GitHub profile ကို သွားကြည့်ရင် အရင်က ပျောက်နေတဲ့ green block လေးတွေ ပြန်ပေါ်လာတာကို တွေ့ရပါလိမ့်မယ်။ ဒီ post က contribution graph မှာ contribution တွေ မပေါ်လို့ စိတ်ညစ်နေရတဲ့ developer တွေအတွက် အထောက်အကူဖြစ်မယ်လို့ မျှော်လင့်ပါတယ်။\n","date":"9 မတ် 2026","externalUrl":null,"permalink":"/posts/github-contributions-missing-gitmailmap/","section":"Posts","summary":"","title":"GitHub မှာ Contribution တွေ မပေါ်ရင် ဘယ်လိုလုပ်မလဲ (gitmailmap)","type":"posts"},{"content":"","date":"9 မတ် 2026","externalUrl":null,"permalink":"/tags/gitmailmap/","section":"Tags","summary":"","title":"Gitmailmap","type":"tags"},{"content":"Read this article in English\nနိဒါန်း # Code push လုပ်တဲ့အခါ commit message တွေကို သေချာရေးဖို့ အရမ်းအရေးကြီးပါတယ်။ ဒါပေမယ့် တခါတလေမှာ commit message က typo ဖြစ်သွားတာမျိုး၊ ဒါမှမဟုတ် ပိုပြီးရှင်းလင်းတဲ့ commit message ကို ပြောင်းချင်တာမျိုးတွေ ကြုံရတတ်ပါတယ်။\nဒီ post မှာတော့ မိမိရေးခဲ့တဲ့ past commits တွေကို git commit --amend နဲ့ git rebase သုံးပြီး ဘယ်လို update လုပ်ရမလဲဆိုတာကို sharing လုပ်ချင်ပါတယ်။\nနောက်ဆုံး Commit ကို ပြင်ချင်တာဆိုရင်? # ကိုယ်ပြင်ချင်တဲ့ commit က latest commit ဖြစ်နေတယ်ဆိုရင်တော့ အလွယ်ဆုံးပါပဲ။\ngit commit --amend command ကိုသုံးပြီး အလွယ်တကူ ပြင်ဆင်နိုင်ပါတယ်။\ngit commit --amend ဒီ command ကို run လိုက်တာနဲ့ terminal မှာ text editor ပွင့်လာပါလိမ့်မယ်။ အဲ့ဒီမှာ commit message အသစ်ကို ပြင်ရေးပြီး save လိုက်ရုံပါပဲ။\nPro Tip: တကယ်လို့ commit message ကို မပြင်ဘဲ file တွေ မေ့ကျန်ခဲ့လို့ နောက်ဆုံး commit ထဲကိုပဲ ထပ်ထည့်ချင်တာဆိုရင်တော့ git add . လုပ်ပြီး git commit --amend --no-edit ဆိုပြီး အလွယ်တကူ သုံးလို့ရပါတယ်။\nနောက်ဆုံးမဟုတ်တဲ့ Commit အဟောင်းတွေကို ပြင်ချင်တာဆိုရင်? # တကယ်လို့ ကိုယ်ပြင်ချင်တဲ့ commit တွေက နောက်ဆုံး commit မဟုတ်ဘဲ အရင်ကလုပ်ခဲ့တဲ့ commit တွေဖြစ်နေတယ်ဆိုရင်တော့ git rebase ကို သုံးဖို့လိုပါတယ်။ ဥပမာ - နောက်ဆုံး commits ၃ ခုကို ပြန်ပြင်ချင်တယ်ဆိုရင် interactive rebase ကို အောက်ပါအတိုင်း သုံးနိုင်ပါတယ်။\ngit rebase -i HEAD~3 ဒီ command ကို run လိုက်တဲ့အခါ editor (maybe Vim) ပွင့်လာပြီး commit list ကို တွေ့ရပါလိမ့်မယ်။\nဥပမာ:\npick e3a1b35 Commit message 1 pick 7ac9a67 Commit message 2 pick 4db8c21 Commit message 3 ကိုယ်ပြင်ချင်တဲ့ commit ရဲ့ရှေ့က pick ဆိုတဲ့စာသားနေရာမှာ reword လို့ ပြောင်းပေးရပါမယ်။ အခု ဥပမာမှာ reword ကို commit တိုင်းအတွက် သုံးပြထားပါတယ်။\nreword e3a1b35 Commit message 1 reword 7ac9a67 Commit message 2 reword 4db8c21 Commit message 3 Save လုပ်ပြီး ထွက်လိုက်တဲ့အခါ reword ပြောင်းထားတဲ့ commit တစ်ခုချင်းစီအတွက် editor က တလှည့်စီပွင့်လာပါလိမ့်မယ်။ အဲ့ဒီမှာ ကိုယ်ပြောင်းချင်တဲ့ commit message အသစ်တွေ လိုက်ပြင်ပြီး save လိုက်ရုံပါပဲ။\nPro Tip: Interactive rebase မှာ reword အပြင် တခြားအသုံးဝင်တဲ့ command တွေလည်း ရှိပါသေးတယ်။ ဥပမာ:\nမလိုအပ်တဲ့ commit ကို ဖျက်ပစ်ချင်ရင် drop (သို့မဟုတ် d) commit နှစ်ခုကို တခုတည်းအဖြစ် ပေါင်းချင်ရင် squash (သို့မဟုတ် s) commit message တင်မဟုတ်ပဲ file တွေကိုပါ ပြင်ချင်တယ်ဆိုရင် edit (သို့မဟုတ် e) rebase လုပ်နေရင်း အမှားတစ်ခုခုဖြစ်သွားလို့ ဆက်မလုပ်တော့ဘူး၊ မူလအတိုင်း ပြန်ထားချင်တယ်ဆိုရင် git rebase --abort ကို သုံးနိုင်ပါတယ်။ Safety Net # Commit history တွေကို rebase လုပ်ရင်း မှားသွားလို့ပဲဖြစ်ဖြစ်၊ မှားဖျက်မိသွားလို့ပဲဖြစ်ဖြစ် မူလအတိုင်း ပြန်လိုချင်တယ်ဆိုရင်လည်း စိတ်မပူပါနဲ့။ git reflog ဆိုတဲ့ command ရှိပါတယ်။\ngit reflog ဒီ command ကတဆင့် ကိုယ်လုပ်ခဲ့သမျှ action history တွေနဲ့ commit hash တွေကို ပြန်ကြည့်လို့ရပါတယ်။ အဲ့ဒီကနေ ကိုယ်ပြန်သွားချင်တဲ့ နေရာကို git reset --hard \u0026lt;commit-hash\u0026gt; သုံးပြီး အလွယ်တကူ ပြန်သွားနိုင်ပါတယ်။\nPush to Remote # Commit တွေ ပြင်ပြီးသွားပြီဆိုရင်တော့ remote repository ကို ပြန် push လုပ်ဖို့ လိုပါတယ်။ ဒါပေမယ့် commit history တွေကို ပြောင်းလိုက်တဲ့အတွက် ရိုးရိုး git push လုပ်လို့ မရတော့ပါဘူး။\nဒီလိုအခြေအနေမျိုးမှာ force push လုပ်ဖို့လိုပေမယ့် တခြားသူတွေရဲ့ code တွေကို မထိခိုက်စေဖို့အတွက် ပို safe ဖြစ်တဲ့ --force-with-lease ကို အမြဲသုံးသင့်ပါတယ်။\ngit push --force-with-lease နိဂုံး # ဒီ command တွေက နေ့စဉ် development workflow မှာ အရမ်းအသုံးဝင်ပါတယ်။ မှားရေးမိတဲ့ commit တွေကို code review မလုပ်ခင် အလွယ်တကူ ပြင်ဆင်နိုင်တာကြောင့် git history ကိုလည်း clean and clear ဖြစ်စေမှာပါ။\n","date":"6 မတ် 2026","externalUrl":null,"permalink":"/posts/update-past-commits/","section":"Posts","summary":"","title":"Past Commits တွေကို ဘယ်လို Update လုပ်မလဲ","type":"posts"},{"content":"","date":"6 မတ် 2026","externalUrl":null,"permalink":"/tags/version-control/","section":"Tags","summary":"","title":"Version-Control","type":"tags"},{"content":"","date":"7 ဇန်နဝါရီ 2026","externalUrl":null,"permalink":"/tags/ai/","section":"Tags","summary":"","title":"Ai","type":"tags"},{"content":"","date":"7 ဇန်နဝါရီ 2026","externalUrl":null,"permalink":"/tags/github-copilot/","section":"Tags","summary":"","title":"Github-Copilot","type":"tags"},{"content":"","date":"7 ဇန်နဝါရီ 2026","externalUrl":null,"permalink":"/tags/react/","section":"Tags","summary":"","title":"React","type":"tags"},{"content":"","date":"7 ဇန်နဝါရီ 2026","externalUrl":null,"permalink":"/tags/uuid/","section":"Tags","summary":"","title":"Uuid","type":"tags"},{"content":"Read this article in English\nနိဒါန်း # AI assistant တွေ ပေါ်လာတာနဲ့အမျှ software development landscape က တော်တော်လေးပြောင်းလဲလာပါတယ်။ အထူးသဖြင့် GitHub Copilot လို tool တွေက simple CRUD app ကနေ complex logic ပါတာတွေအထိ implementation အပိုင်းကို handle လုပ်ပေးနိုင်နေပါပြီ။ Developer တွေအနေနဲ့ high-level requirements တွေကိုပဲ focus လုပ်ဖို့ လိုပါတော့တယ်။\nဒီ post မှာတော့ GitHub Copilot ကိုသုံးပြီးရေးထားတဲ့ UUID Generator project အကြောင်း sharing လုပ်ချင်ပါတယ်။\nUUID Generator Features # UUID Generator ကို React 19, Vite နဲ့ Tailwind CSS သုံးပြီး build လုပ်ထားပါတယ်။ အဓိက feature တွေကတော့-\nUUID Version Support: V1 (timestamp-based)၊ V4 (random) နဲ့ V7 (time-ordered) ကို support ပေးပါတယ်။ Batch Generation: တခါ generate လုပ်ရင် UUID အခု 200 အထိ ရနိုင်ပါတယ်။ UI ပေါ်မှာတော့ performance အတွက် 20 ခုပဲ preview ပြပေးပါတယ်။ Smart Controls: Batch size နဲ့ preview count ကို unified slider နဲ့ control လုပ်နိုင်ပါတယ်။ Copy \u0026amp; Download: Clipboard copy နဲ့ text file download (timestamped filename နဲ့) ရပါတယ်။ Formatting: Uppercase, remove hyphens, wrap braces စတာတွေ support လုပ်ပါတယ်။ Breaking Down # 1. AI-First Development # ဒီ project ရဲ့ ထူးခြားချက်က core implementation အားလုံးနီးပါးကို GitHub Copilot (GPT-5.1-Codex) က ရေးပေးထားတာပါ။ Human inputs အနေနဲ့က high-level requirements, code review နဲ့ testing guidance လောက်ပဲ ပေးရပါတယ်။ React architecture, Tailwind styling, Business logic နဲ့ Unit tests တွေကို AI က အစအဆုံး implement လုပ်သွားပါတယ်။\n2. Prompt Evolution # Workflow က traditional planning -\u0026gt; coding ပုံစံမဟုတ်ဘဲ conversational iteration ပုံစံနဲ့သွားပါတယ်။ README မှာလည်း document လုပ်ထားတဲ့ prompt history ကိုကြည့်ရင် feature တခုချင်းစီကို incremental build လုပ်သွားတာ တွေ့ရပါလိမ့်မယ်။\nInitial Interface styling Version selector logic Mobile responsive badges UX interactions (copy feedback) Scaling to 200 items Unified slider control Information architecture refinement 3. Technical Highlights # Copilot ရေးပေးတဲ့ code quality ကလည်း မဆိုးဘူးလို့ ပြောလို့ရပါတယ်။\nCustom Hooks: logic တွေကို useTheme နဲ့ useUuidGenerator ဆိုပြီး ခွဲထုတ်ထားတဲ့အတွက် clean code ဖြစ်သလို test လုပ်ရတာလည်း လွယ်ကူပါတယ်။ Web Crypto API: True randomness ရဖို့ Math.random() အစား crypto.randomUUID() ကို web crypto API ကနေတဆင့် သုံးထားပါတယ်။ Optimized Rendering: item 200 လုံးကို render မလုပ်ဘဲ viewport ထဲက item တွေကိုပဲပြပေးတဲ့ logic တွေပါ ထည့်သွင်းစဉ်းစားပေးထားပါတယ်။ နိဂုံး # ဒီ project က AI-assisted development workflow ကို စမ်းသပ်ဖို့ ကောင်းတဲ့ example ပါ။ Developer အနေနဲ့ implementation details ထက် architectural vision နဲ့ quality assurance ပေါ် ပို focus လုပ်နိုင်တာကို တွေ့ရပါတယ်။ Project source code ကို GitHub မှာ လေ့လာနိုင်ပြီး Demo ကိုလည်း ဝင်ရောက်စမ်းသပ်ကြည့်နိုင်ပါတယ်။\n","date":"7 ဇန်နဝါရီ 2026","externalUrl":null,"permalink":"/posts/uuid-generator-with-ai-agent/","section":"Posts","summary":"","title":"UUID Generator With AI Agent","type":"posts"},{"content":"","date":"7 ဇန်နဝါရီ 2026","externalUrl":null,"permalink":"/tags/vite/","section":"Tags","summary":"","title":"Vite","type":"tags"},{"content":"","date":"23 ဒီဇင်ဘာ 2025","externalUrl":null,"permalink":"/tags/github-actions/","section":"Tags","summary":"","title":"Github-Actions","type":"tags"},{"content":"Read this article in English\nနိဒါန်း # ပြီးခဲ့တဲ့ post မှာ Go project တွေအတွက် linter workflow အကြောင်း ပြောပြခဲ့ပြီးပါပြီ။ Code quality ကောင်းဖို့အတွက် linting က အရေးကြီးသလိုပဲ၊ ကိုယ်ရေးထားတဲ့ code က မျှော်မှန်းထားတဲ့အတိုင်း အလုပ်လုပ်ရဲ့လား၊ feature အသစ်တွေထည့်လိုက်ရင် အဟောင်းတွေကရော အလုပ်လုပ်သေးရဲ့လားဆိုတာ သေချာဖို့အတွက် testing ကလည်း မရှိမဖြစ် လိုအပ်ပါတယ်။\nကျွန်တော် ဒီ post မှာတော့ လက်ရှိ project တွေမှာ သုံးနေတဲ့ unit testing အတွက် GitHub Actions workflow အကြောင်း ပြောပြချင်ပါတယ်။\nAction File For Unit Tests # ဒါကတော့ Go project တွေမှာ unit tests run ဖို့နဲ့ code coverage တွက်ဖို့အတွက် ကျွန်တော်သုံးနေတဲ့ workflow file ပါ။\nname: Unit Tests on: push: branches: [main] paths: - \u0026#34;cmd/**\u0026#34; - \u0026#34;pkg/**\u0026#34; - \u0026#34;go.mod\u0026#34; - \u0026#34;go.sum\u0026#34; - \u0026#34;Makefile\u0026#34; pull_request: branches: [main] paths: - \u0026#34;cmd/**\u0026#34; - \u0026#34;pkg/**\u0026#34; - \u0026#34;go.mod\u0026#34; - \u0026#34;go.sum\u0026#34; - \u0026#34;Makefile\u0026#34; jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Set up Go uses: actions/setup-go@v6 with: go-version: \u0026#34;1.25.5\u0026#34; # Caching prevents redownloading dependencies on every run until # go.mod/go.sum changes - name: Cache Go modules uses: actions/cache@v5 with: path: | ~/go/pkg/mod # Go modules cache ~/.cache/go-build # Go build cache key: ${{ runner.os }}-go-${{ hashFiles(\u0026#39;**/go.sum\u0026#39;) }} - name: Run unit tests run: make test - name: Run tests with coverage run: make test-coverage - name: Upload coverage to Codecov uses: codecov/codecov-action@v5 with: files: ./coverage.out flags: unittests fail_ci_if_error: false Breaking Down # 1. Staring on: # Linter workflow လိုပါပဲ၊ ဒီ workflow က main branch ကို code push လုပ်တဲ့အခါ ဒါမှမဟုတ် pull request တင်တဲ့အခါတွေမှာ အလုပ်လုပ်ပါလိမ့်မယ်။ Code နဲ့ဆိုင်တဲ့ file တွေ (cmd/**၊ pkg/** စသဖြင့်) နဲ့ build၊ dependency အပိုင်းနဲ့ဆိုင်တဲ့ Makefile၊ go.mod၊ go.sum တွေ ပြင်မှသာ ဒီ test workflow ကို run မှာဖြစ်လို့ မလိုအပ်ဘဲ workflow run နေတာမျိုး မဖြစ်အောင် ကာကွယ်ပေးပါတယ်။\n2. Setup and Caching # ubuntu-latest ပေါ်မှာ Go v1.25.5 ကို setup လုပ်ပြီး run ပါတယ်။ Caching အပိုင်းကတော့ အရင် post မှာ ပြောခဲ့သလိုပဲ go.sum file မှာ အပြောင်းအလဲမရှိသ၍ Go modules တွေကို download မလုပ်ဘဲ cache ကနေ ပြန်ယူသုံးတဲ့အတွက် workflow ကြာချိန်ကို သိသိသာသာ လျှော့ချပေးပါတယ်။\n3. Running Tests # ဒီ workflow မှာ test command တွေကို run ဖို့အတွက် Makefile ကို သုံးထားပါတယ်။ ဒါကြောင့် ကိုယ့် project ရဲ့ Makefile ထဲမှာ သက်ဆိုင်ရာ command တွေ ရှိနေဖို့တော့လိုပါမယ်။\nmake test: ဒါက ပုံမှန် unit test တွေကို run ဖို့အတွက်ပါ။ ဥပမာ go test ./... လိုမျိုး။ make test-coverage: ဒါကတော့ test coverage တွက်ဖို့နဲ့ report file (coverage.out) ထွက်လာအောင် run တာပါ။ 4. Uploading Test Coverage # - name: Upload coverage to Codecov uses: codecov/codecov-action@v5 with: files: ./coverage.out flags: unittests fail_ci_if_error: false Test coverage တွက်ပြီးရင် ရလာတဲ့ result ကို visually ကြည့်လို့ရအောင် Codecov ကို upload တင်ပါတယ်။ files: ./coverage.out ဆိုပြီး step #4 မှာ ထွက်လာတဲ့ report file ကို ညွှန်းပေးထားပါတယ်။ fail_ci_if_error: false ကတော့ တခုခုကြောင့် codecov upload တင်တာ error တက်ခဲ့ရင်လည်း အဓိက test run တာ အောင်မြင်တယ်ဆိုရင် CI workflow ကို မ fail စေချင်လို့ပါ။\nနိဂုံး # ဒီ test workflow ထည့်ထားခြင်းအားဖြင့် PR တင်တဲ့အခါမှာ ကိုယ့် code ကြောင့် ရှိပြီးသား functionality တွေ ပျက်မသွားဘူးဆိုတာတဲ့ ယုံကြည်မှုမျိုး ရနိုင်မှာပါ။။ Test coverage report ကိုကြည့်ပြီးတော့လည်း ကိုယ့် project ရဲ့ ဘယ်အပိုင်းတွေမှာ unit test လိုနေသေးလဲဆိုတာ သိနိုင်ပါတယ်။ CI/CD pipeline မှာ testing က မပါမဖြစ်မို့လို့ ထည့်သုံးကြည့်ကြဖို့ တိုက်တွန်းချင်ပါတယ်။\n","date":"23 ဒီဇင်ဘာ 2025","externalUrl":null,"permalink":"/posts/unit-test-with-github-actions/","section":"Posts","summary":"","title":"Unit Test With GitHub Actions","type":"posts"},{"content":"Read this article in English\nနောက်ဆုံး blog post ရေးခဲ့တာက ပြီးခဲ့တဲ့ဇန်နဝါရီလကဆိုတော့ ဘာမှမရေးဖြစ်ခဲ့တာ တနှစ်နီးပါးလောက်တောင် ရှိသွားခဲ့ပါပြီ။ ဒီကြားထဲမှာ ကိုယ်ရေးကိုယ်တာကိစ္စတွေနဲ့ အာရုံများနေတာရော ပျင်းနေတာရောပါတယ်။ ဒီရက်ပိုင်းမှ Bubble Tea ကို စမ်းကြည့်ချင်တာရော personal income tax တွက်ဖို့ CLI app လေးတခုရေးချင်တာရောနဲ့ PIT Calculator CLI app လေးလုပ်ဖြစ်တယ်။ ရိုးရိုး CLI interface နဲ့ TUI interface ဆိုပြီး နှစ်ခုနဲ့ စမ်းကြည့်လို့ရပါတယ်။ Copilot အကူအညီနဲ့ ပါ README သေချာရေးဖြစ်သွားတော့ အဆင်ပြေရင် စမ်းကြည့်ကြစေချင်ပါတယ်။ Life insurance နဲ့ တခြား အခွန်သက်သာခွင့်တွေရယ် bonus နဲ့ တခြား taxable income တွေအတွက်ရယ်တော့ ထပ်ထည့်ဖို့တော့ ကျန်ပါဦးမယ်။\nနိဒါန်း # Software development မှာ code quality နဲ့ consistency ဆိုတာ မဖြစ်မနေလိုအပ်တဲ့အရာတွေပါ။ Team နဲ့လုပ်ရတဲ့ project တွေမှာဆို coding style တယောက်တမျိုးစီရေးကြတာတွေကြောင့် ဖတ်ရခက်တာမျိုးတွေ၊ bug ဖြစ်စေနိုင်တဲ့ ပုံစံမျိုးတွေ မပါဖို့နဲ့ unreachable code တွေ မရှိနေဖို့ အတွက် PR နဲ့ stable branch ကို code merge တာတွေမှာ linting က အရေးကြီးပါတယ်။\nအခု post မှာတော့ PIT Calculator CLI app မှာသုံးထားတဲ့ linter အတွက် workflow အကြောင်းကွက်ပြီး ပြောပြချင်လို့ပါ။ အလုပ်မှာကတော့ သုံးဖြစ်ပေမည့် personal project မှာတော့ Hugo ရဲ့ deployment အတွက်ကလွဲရင် အခုမှစသုံးဖြစ်တာပါ။\nWorkflow For Lint # ဒါက ကျွန်တော်လက်ရှိသုံးဖြစ်နေတဲ့ Go အတွက် linter workflow ပါ။\nname: Lint permissions: contents: read on: push: branches: [main] paths: - \u0026#34;cmd/**\u0026#34; - \u0026#34;pkg/**\u0026#34; - \u0026#34;go.mod\u0026#34; - \u0026#34;go.sum\u0026#34; - \u0026#34;.github/workflows/lint.yml\u0026#34; pull_request: branches: [main] paths: - \u0026#34;cmd/**\u0026#34; - \u0026#34;pkg/**\u0026#34; - \u0026#34;go.mod\u0026#34; - \u0026#34;go.sum\u0026#34; - \u0026#34;.github/workflows/lint.yml\u0026#34; jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Set up Go uses: actions/setup-go@v6 with: go-version: \u0026#34;1.25.5\u0026#34; # Caching prevents redownloading dependencies on every run until # go.mod/go.sum changes - name: Cache Go modules uses: actions/cache@v5 with: path: | ~/go/pkg/mod # Go modules cache ~/.cache/go-build # Go build cache key: ${{ runner.os }}-go-${{ hashFiles(\u0026#39;**/go.sum\u0026#39;) }} # Ensures gofmt sees only actual Go code that belongs to your module - name: Run go fmt run: | FILES=$(gofmt -s -l $(go list -f \u0026#39;{{.Dir}}\u0026#39; ./...)) if [ -n \u0026#34;$FILES\u0026#34; ]; then echo \u0026#34;❌ Formatting issues found:\u0026#34; echo \u0026#34;$FILES\u0026#34; exit 1 fi echo \u0026#34;✅ Code formatting is correct\u0026#34; - name: Run go vet run: | go vet ./... echo \u0026#34;✅ Code analysis passed\u0026#34; - name: Check for imports run: | go mod tidy if [ -n \u0026#34;$(git diff --name-only go.mod go.sum)\u0026#34; ]; then echo \u0026#34;❌ go.mod/go.sum are not tidy. Run \u0026#39;go mod tidy\u0026#39; to fix:\u0026#34; git diff exit 1 fi echo \u0026#34;✅ Dependencies are tidy\u0026#34; Breaking Down # 1. Staring on: # on: push: branches: [main] paths: - \u0026#34;cmd/**\u0026#34; - \u0026#34;pkg/**\u0026#34; - \u0026#34;go.mod\u0026#34; - \u0026#34;go.sum\u0026#34; - \u0026#34;.github/workflows/lint.yml\u0026#34; pull_request: branches: [main] paths: - \u0026#34;cmd/**\u0026#34; - \u0026#34;pkg/**\u0026#34; - \u0026#34;go.mod\u0026#34; - \u0026#34;go.sum\u0026#34; - \u0026#34;.github/workflows/lint.yml\u0026#34; ဒီ on: အပိုင်းက workflow ကို ဘယ်အချိန်မှာစအလုပ်လုပ်မယ်ဆိုတာကို သတ်မှတ်ထားတာပါ။\npush နဲ့ pull_request နှစ်ခုလုံးမှာ အလုပ်လုပ်ပါမယ်။ branches: [main] ဆိုတာက main branch ကို code push တာ ဒါမှမဟုတ် main branch ကို PR တင်တာမျိုးလုပ်မှသာ အလုပ်လုပ်ပါလိမ့်မယ်။ paths: မှာ cmd/**၊ pkg/** အစရှိတဲ့ Go file တွေ ဒါမှမဟုတ် workflow file lint.yml ကို ပြင်မှသာ ဒီ workflow ကို run မှာဖြစ်လို့ လိုအပ်တဲ့အချိန်မှာပဲ run ပြီး GitHub Actions အတွက် ကုန်ကျစရိတ်ကိုလျှော့ချလို့ရပါတယ်။ 2. Preparing Environment And Caching # runs-on: ubuntu-latest: Job ကို Ubuntu latest environment ပေါ်မှာ run ပါမယ်။ uses: actions/setup-go@v6: Go v1.25.5 ကို install လုပ်ပါမယ်။ actions/cache@v5: go.sum file မှာ အပြောင်းအလဲမရှိသ၍ Go modules တွေကို ပြန်ပြီး download လုပ်စရာမလိုတော့ဘဲ cache ကနေ ပြန်ယူသုံးတဲ့အတွက် build time ကို သိသိသာသာ လျှော့ချပေးပါတယ်။ အချိန်ကုန်ငွေကုန်သက်သာစေတဲ့ အဓိကအချက်ပါ။ 3. Code Formatting # FILES=$(gofmt -s -l $(go list -f \u0026#39;{{.Dir}}\u0026#39; ./...)) if [ -n \u0026#34;$FILES\u0026#34; ]; then echo \u0026#34;❌ Formatting issues found:\u0026#34; echo \u0026#34;$FILES\u0026#34; exit 1 fi echo \u0026#34;✅ Code formatting is correct\u0026#34; gofmt -s -l: သေချာ format မလုပ်ထားတဲ့ file list ကို ထုတ်ပေးပါတယ်။ if [ -n \u0026quot;$FILES\u0026quot; ]; then: $FILES ထဲမှာ file တွေပါနေတယ်ဆိုရင် workflow ကို exit 1 နဲ့ fail လိုက်မှာပါ။ ဒါက format မမှန်တဲ့ code တွေ main branch ထဲရောက်မသွားအောင် ကာကွယ်ထားတာပါ။ 4. Static Code Analysis # go vet ./...: format string မှားတာ(%s နေရာမှာ %d လိုမျိုး format specifier မှားသုံးထားတာမျိုး)တွေ၊ shadowed variable တွေ၊ struct tag မှားသုံးထားတာတွေ၊ unreachable code တွေနဲ့ unsafe conversion တွေကို စစ်ပေးတာပါ။\n5. Checking Unused Dependencies # go mod tidy if [ -n \u0026#34;$(git diff --name-only go.mod go.sum)\u0026#34; ]; then echo \u0026#34;❌ go.mod/go.sum are not tidy. Run \u0026#39;go mod tidy\u0026#39; to fix:\u0026#34; git diff exit 1 fi echo \u0026#34;✅ Dependencies are tidy\u0026#34; go mod tidy: မလိုအပ်တဲ့ dependency တွေကို ရှင်းပေးပြီး လိုတာတွေကိုထည့်ပေးပါတယ်။ if [ -n \u0026quot;$(git diff --name-only go.mod go.sum)\u0026quot; ]; then: git diff နဲ့ go.mod ဒါမှမဟုတ် go.sum file တွေမှာ ပြောင်းလဲတာရှိမရှိ စစ်တာပါ။ ပြောင်းလဲတာရှိရင် (ဆိုလိုတာက developer က go mod tidy ကို မ run ခဲ့ဘူး) လို့ ယူဆပြီး workflow ကို exit 1 နဲ့ fail လိုက်မှာပါ။ နိဂုံး # ဒီ linting workflow က code quality နဲ့ issue ကို ၁၀၀% မစစ်ပေးနိုင်ပါဘူး။ PR review လုပ်တဲ့အခါမှာ အကူအညီဖြစ်အောင်နဲ့ reviewer မမြင်လိုက်တာတွေ ပါမသွားအောင် တဘက်တလမ်းကနေ ကူညီနိုင်တာပဲရှိတာပါ။ မှန်ကန်တဲ့ design pattern တွေသုံးဖို့၊ maintainability ကို ကျစေတဲ့ cognitive complexity နည်းဖို့တွေကတော့ ကိုယ်တိုင်ပဲလုပ်ရမှာပါ။ နောက်ပိုင်းကျရင် တခြားအသုံးဝင်တဲ့ workflow တွေလည်း ထပ်ရေးကြည့်ပါဦးမယ်။ အခုလက်ရှိ repo တွေမှာလည်း ကြည့်လို့ရပါတယ်။\n","date":"13 ဒီဇင်ဘာ 2025","externalUrl":null,"permalink":"/posts/go-linter-with-github-actions/","section":"Posts","summary":"","title":"Go Linter With GitHub Actions","type":"posts"},{"content":"","date":"22 ဇန်နဝါရီ 2025","externalUrl":null,"permalink":"/tags/development/","section":"Tags","summary":"","title":"Development","type":"tags"},{"content":"Read this article in English\nGo လိုမျိုး compiled language အတော်များများမှာ hot reload က natively မပါတာများပါတယ်။ Flutter ကတော့ ချွင်းချက်ပေါ့။ အဲ့တော့ developer တွေအနေနဲ့က code ရေးလိုက် terminal မှာ ပြန်ပြီး recompile လုပ်လိုက်နဲ့က ပုံမှန်ဆို မသိသာပေမယ့် အကြိမ်ရေများလာတဲ့အခါကျ တော်တော်လေး စိတ်အနောင့်အယှက်ဖြစ်ရတယ်။ ကျွန်တော့လိုမျိုး code editor နဲ့ terminal နဲ့ကို သပ်သပ်စီသုံးတတ်တဲ့သူတွေကတော့ ပိုဆိုးမယ်ထင်ပါတယ်။\nအဓိကပြဿနာတခုက context switching ပါ။ Complex ဖြစ်ပြီးတော့ nested iteration တွေပါတဲ့ code ကို develop/debug လုပ်နေရတာမျိုးမှာ အာရုံစိုက်ရခက်ပါတယ်။\nအကြောင်းကြောင်းကြောင့် Docker container ထဲမှာ compile လုပ်ပြီး စမ်းဖို့လိုလာရင်လည်း လက်ရှိ code ကို volume အနေနဲ့ mount လုပ်နေရတာကလည်း အလုပ်ပိုပါတယ်။\nAir for Hot Reloading # ကျွန်တော်ကတော့ live reloading အတွက် Air ကိုသုံးပါတယ်။ ဘာကောင်းလို့ ညာကောင်းလို့ဆိုတာထက် ex-coworker တရောက်ကစသုံးတာနဲ့ လိုက်သုံးဖြစ်လိုက်တာပါပဲ။ တချို့ အဆင်မပြေတာတွေရှိပေမဲ့လည်း ကျွန်တော်အခုစမ်းရေးနေတဲ့ image processing server အတွက်လည်း Air ပဲသုံးထားလိုက်ပါတယ်။\nInstall လုပ်တာကလွယ်ပါတယ်။ go install github.com/air-verse/air@latest နဲ့ install လုပ်၊ configuration file အနေနဲ့ .air.toml file ကို set up လုပ်ပြီး air -c .air.toml နဲ့ project ကို run လိုက်ရုံပါပဲ။ air init နဲ့ .air.toml file ကို initiate လုပ်လို့လည်းရပါတယ်။\nAir in Dockerfile # ကျွန်တော်ကတော့ Docker နဲ့ run ရတာများတဲ့အတွက် Dockerfile ထဲမှာ Air ကို install လုပ်ပြီး Docker Compose နဲ့ codebase directory ကို mount လုပ်ပြီးသုံးပါတယ်။\nDockerfile\nFROM golang:1.23.4-bookworm WORKDIR /app RUN go install github.com/air-verse/air@latest COPY . . RUN go mod download CMD [\u0026#34;air\u0026#34;, \u0026#34;-c\u0026#34;, \u0026#34;.air.toml\u0026#34;] docker-compose.yaml\nservices: app: build: context: . dockerfile: Dockerfile env_file: - .env ports: - 8080:8080 volumes: - ./:/app တခုသတိထားသင့်တာက အခု post ကိုရေးနေတဲ့အချိန်မှာ Air ရဲ့ latest release က Go v1.23 အောက်ကို support မလုပ်ပါဘူး။ ကိုယ်က အကြောင်းကြောင်းကြောင့် Go version အနိမ့်မှာသုံးဖို့လိုလာရင်တော့ Air version အနိမ့်ကို install လုပ်ဖို့လိုပါမယ်။\nLast But Not Least # .air.toml ထဲက rerun_delay ရဲ့ value ကို လိုအပ်သလို စက္ကန့်ပိုင်းလောက်ပိုထားဖို့တော့ အကြံပေးချင်ပါတယ်။ မဟုတ်ရင် code changes ကို save လိုက်တာနဲ့ မလိုပဲနဲ့ တန်းပြီး build လုပ်နေပါလိမ့်မယ်။\n","date":"22 ဇန်နဝါရီ 2025","externalUrl":null,"permalink":"/posts/hot-reload-in-go/","section":"Posts","summary":"","title":"Hot Reload in Go","type":"posts"},{"content":"Read this article in English\nMobile နဲ့ web app ရေးတဲ့သူတွေ၊ CURD API ရေးဖူးတဲ့သူတွေနဲ့ third-party API သုံးပြီးရေးဖူးတဲ့ programmer တွေဆိုရင် pagination ကို ရင်းရင်းနီးနီးသိကြမှာပါ။ ကိုယ်က API ရေးတဲ့သူဆိုရင် URL မှာ limit နဲ့ skip လိုမျိုး query parameter တွေထည့်ပေးတာမျိုး၊ validation အနေနဲ့ဆိုရင် limit မှာ အများဆုံး ဘယ်လောက်ပဲပေးထားတာမျိုး စသည်ဖြင့်ပေါ့။ Mobile developer တွေဆိုရင်လည်း infinite scroll လိုမျိုးတွေမှာ user က scroll လုပ်တာဘယ်နေရာကိုရောက်နေရင် နောက်တ page အတွက် API ကိုကြိုခေါ်ရမယ် ဘာညာပေါ့။ ဒါတွေက အားလုံးသိပြီးသားဖြစ်လောက်တဲ့အတွက် လိုရင်းကိုပဲသွားလိုက်ပါမယ်။\nOffset Pagination # Cursor pagination ဆိုပြီး ဘာလို့ skip နဲ့ limit သုံးတဲ့ pagination အကြောင်းအရင်ပြောရလဲဆိုရင် အသုံးအများဆုံး pagination ဖြစ်နေလို့ပါ။ သူအလုပ်လုပ်တဲ့ပုံစံက ရှင်းပါတယ်။ Query parameter အနေနဲ့ပြောရမယ်ဆို ?limit=20\u0026amp;skip=40 လို့ပေးလိုက်ရင် sort လုပ်ထားပြီးတဲ့အထဲကနေ ပထမ record ၄၀ ကို ဖယ်ပြီး record ၂၀ ပြန်ပေးလိုက်ရုံပါပဲ။\nSQL အနေနဲ့ရေးရင်လည်း လွယ်လွယ်ကူကူ ရေးလို့ရပါတယ်။\nSELECT * FROM records OFFSET 20 LIMIT 20; MongoDB မှာဆိုရင်လည်း အဲ့လိုပါပဲ။\ndb.records.find({}, { limit: 20, skip: 20 }) Drawbacks of Offset Pagination # Offset pagination မှာအဓိက အားနည်းချက် နှစ်ခုရှိပါတယ်။\n1. Performance Issue on Large Dataset # ပထမတခုက performance issue ပါ။ Dataset ကအရမ်းကြီးလာပြီဆိုရင် OFFSET က ပြဿနာပေးလာပါတယ်။ လိုချင်တဲ့ data ကိုမထုတ်ပေးခင် table တခုလုံးကို scan လုပ်ဖို့လိုတဲ့အတွက်ပါ။ Query က complex ဖြစ်နေရင် ပိုတောင်ဆိုးပါသေးတယ်။ Database တခုထဲသုံးထားတာမျိုးဆို အဲ့လို query မျိုးကြောင့် system တခုလုံးရဲ့ performance ကိုပါ ထိခိုက်စေပါတယ်။ Microservices မှာဆိုရင်တော့ service တခုလုံးပေါ့။\n2. Inconsistent Data When Insert/Delete # ဒုတိယတခုက consistent ဖြစ်တဲ့ data ကိုမရတာပါ။ ဒီတခုက အပြောင်းအလဲသိပ်မများတဲ့ system မျိုးမှာဆိုရင် သိပ်မသိသာပါဘူး။ Social media app မျိုး၊ B2C နဲ့ C2C marketplace app မျိုးမှာဆိုရင် consistent ဖြစ်တဲ့ data ကို မပေးနိုင်တာက တကယ့်ပြဿနာပါ။\nဥပမာအနေနဲ့ပြောရရင် database table ထဲမှာ ID 1 ကနေ 100 အထိ record ၁၀၀ ရှိပြီး၊ descending order နဲ့ data ပြန်ပေးမယ်ဆိုကြပါစို့။ ပထမဆုံးအကြိမ်မှာ OFFSET 0 LIMIT 20 နဲ့ data တောင်းလိုက်ရင် ID 100 ကနေ 81 အထိ record ၂၀ ရလာပါမယ်။ ဒုတိယအကြိမ်မှာ OFFSET 20 LIMIT 20 နဲ့ data ထပ်တောင်းမှာဖြစ်တဲ့အတွက် ID 80 ကနေ 61 အထိ record ၂၀ ထပ်ရလာပါမယ်။ ဒီအထိ အားလုံးအဆင်ပြေနေပါသေးတယ်။\nအဲ့ဒီမှာ ဒုတိယအကြိမ်မတိုင်ခင် ID 101 နဲ့ data အသစ်ထပ်ဝင်လာရင် ပြဿနာစပါပြီ။ OFFSET 20 LIMIT 20 ဖြစ်တဲ့အတွက် ID 101 ကနေ 82 အထိ record ၂၀ ကိုကျော်လိုက်ပြီး ID 81 ကနေ 62 အထိ record ၂၀ ကို ပြန်ပေးလိုက်မှာပါ။ Infinite scroll မှာဆိုရင် ID 81 က နှစ်ခုပြပြီး ထပ်သွားပါပြီ။ QA တယောက်ထဲက စစ်နေရတဲ့ မျိုးမှာဆိုရင် testing အဆင့်မှာ မတွေ့ပဲကျော်သွားတတ်ပါတယ်။\nRecord တခုကို ဖျက်လိုက်ရင်လည်း အဲ့လိုပါပဲ။ ဒုတိယအကြိမ်မတိုင်ခင် ID 100 ကိုဖျက်လိုက်တယ် ဆိုကြပါစို့။ OFFSET 20 LIMIT 20 နဲ့ data ယူလိုက်ရင် ID 79 ကနေ 60 အထိပဲပေးပြီး ID 80 က မပါလာတော့ပဲ ကျော်သွားမှာပါ။ ဒါက hard delete ပဲဖြစ်ဖြစ် deleted_at column သုံးတဲ့ soft delete ပဲဖြစ်ဖြစ် ကြုံရမယ့်ပြဿနာပါ။\nCursor Pagination # Cursor pagination ကတော့ offset လိုမျိုးမဟုတ်ပဲ ULID ဒါမှမဟုတ် created_at လိုမျိုး timestamp တွေကိုသုံးပြီး paginate လုပ်တာမျိုးပါ။\nဥပမာ SQL မှာဆိုရင် ဒီလိုမျိုးရေးလို့ရပါတယ်။\nSELECT * FROM records WHERE created_at \u0026lt; 1736533304 LIMIT 20; MongoDB မှာဆိုရင်တော့ အခုလိုမျိုးပေါ့။\ndb.records.find( { created_at: { $lt: 1736533304 } } ).limit(20); Pros of Cursor Pagination # အခုလိုမျိုး condition-based ပုံစံ query ရေးလို့ရတဲ့အတွက် offset pagination မှာဖြစ်ခဲ့တဲ့ ပြဿနာတွေကို ဖြေရှင်းနိုင်သွားပါတယ်။\n1. Efficient Queries and Index Utilization # OFFSET ကို မသုံးရတော့တဲ့အတွက် table/collection တခုလုံးကို scan လုပ်စရာမလိုတော့တဲ့အတွက် query performance ပိုကောင်းလာပါမယ်။\nCondition-based ပုံစံနဲ့ skip လုပ်တဲ့အတွက် pagination ကိုပါတွက်ပြီး index တွေဆောက်လို့ရသွားပါတယ်။ SQL ပဲဖြစ်ဖြစ် NoSQL ပဲဖြစ်ဖြစ် OFFSET အတွက် index ဆောက်လို့မရပါဘူး။\n2. Consistent Data When Insert/Delete # Offset pagination နဲ့ပြောင်းပြန် insert/delete ကြောင့် data တွေပျောက်သွားတာ၊ ထပ်နေတာတွေမရှိတဲ့၊ consistent ဖြစ်တဲ့ data ကိုရနိုင်ပါတယ်။ Insert/delete က ဘယ်လောက်ပဲ frequency မြင့်နေသည်ဖြစ်စေ data loss မဖြစ်နိုင်တော့ပါဘူး။\nနောက်တခေါက်ထပ်ပြောရရင် data အသစ်ဝင်တာ၊ ဖျက်တာ ခဏခဏမရှိတဲ့ system မျိုးမှာဆိုရင် ဒါကိုထည့်မစဉ်းစားလည်း ရနိုင်ပါသေးတယ်။\nDrawbacks of Cursor Pagination # Cursor pagination ကလည်း silver bullet မဟုတ်တဲ့အတွက် အားနည်းချက်တွေရှိပါတယ်။\n1. Complexity # Cursor pagination က offset pagination ထက်စာရင် implement လုပ်ရတာ ပိုရှုပ်ပါတယ်။ ထည့်တွက်ရတာတွေလည်း ပိုများပါတယ်။ ဥပမာ AUTO_INCREMENT ID ကို မသုံးချင်ဘူး၊ created_at လို column မျိုးကိုလည်း အကြောင်းအမျိုးမျိုးကြောင့် မသုံးချင်ဘူးဆိုရင် UUID v7 လိုမျိုးကိုသုံးရမယ်၊ client ဘက်က request လုပ်လိုက်တဲ့ cursor က invalid ဖြစ်ပြီး data ထွက်မလာခဲ့ရင် business logic ပေါ်မူတည်ပြီး ဘယ် data ကို ပြန်ပေးမလဲ ထပ်ရေးရမယ်စသည်ဖြင့်ပေါ့။\n2. Inflexibility # အစဉ်လိုက်ပဲသွားလို့ အဆင်ပြေပါမယ်။ Page 1 ကနေ page 5 ကိုသွားမယ်ဆိုတာမျိုး လုပ်လို့မရပါဘူး။ မရဘူးဆိုတာထက် ရအောင်လို့ ကြံဖန်လုပ်ရမယ့်သဘောပါ။ ရခဲ့ရင်တောင် efficient မဖြစ်ပါဘူး။ နောက်တခုက ပြောင်းပြန်ပြန်သွားရခက်ပါတယ်။ ဥပမာ page 5 ကိုရောက်နေရာကနေ အရင်လာခဲ့တဲ့ page 4 ကိုပြန်သွားချင်တယ်ဆိုရင် မရပါဘူး။ Local storage လိုမျိုးမှာ အရှေ့ကသွားခဲ့တဲ့ page တွေရဲ့ data ကို မှတ်ထားမှရပါမယ်။\nတခုရှိတာက infinite scroll မှာဆိုရင်တော့ ဒီပြဿနာတွေက မရှိသလောက်ပါပဲ။\n3. Depends on Sort and Order By # ဒီတခုက inflexibility နဲ့ သွားဆက်စပ်ပါတယ်။ ဥပမာ page 5 လောက်ရောက်နေတုန်းမှာ name column လိုမျိုးနဲ့ ပြောင်းပြီး sort လုပ်မယ်ဆိုတာမျိုး၊ ascending ကနေ descending order ပြောင်းမယ်ဆိုတာမျိုး လုပ်လို့မရပါဘူး။ ရအောင်လုပ်လည်း efficient မဖြစ်ပါဘူး။ ကပ်သီးကပ်သပ် ရအောင်လုပ်နိုင်တာတွေရှိလို့ ကြိုပြောထားရတာပါ။\n4. Misc # နောက်တခုက ဖြစ်နိုင်ချေနည်းပေမဲ့ ထည့်စဉ်းစားထားသင့်ပါတယ်။ Sort လုပ်ဖို့သုံးမယ့် column တွေက ထပ်လို့မရပါဘူး။ ID လိုမျိုးမှာတော့ကိစ္စမရှိပေမယ့် created_at column မှာဆိုရင် millisecond အထိထပ်သွားနိုင်ချေရှိနေပါတယ်။\nConclusion # အနှစ်ချုပ်အနေနဲ့ပြောရရင် ဘာလာလာ ဒါပဲသုံးမယ်ဆိုတဲ့ silver bullet solution မျိုးမရှိပါဘူး။ System ရဲ့ သဘာဝပေါ်မူတည်ပြီး သင့်တော်တာကိုသုံးသွားရမှာပါ။\nဒီ API က mobile မှာပဲသုံးမှာမလို့ cursor pagination ပဲသုံးလိုက်မယ်လုပ်ရင် web version ထုတ်ချင်တဲ့အချိန်ကျ တိုင်ပတ်ပါလိမ့်မယ်။ Insert/delete က သိပ်မရှိပါဘူးဆိုပြီး offset pagination သုံးထားရင်လည်း frequency များလာရင် ဒုက္ခများမှာပါ။\nConsumer app လိုမျိုးမှာဆိုရင် force update လုပ်ဖို့က ထင်သလောက်မလွယ်ပါဘူး။ User base ကြီးရင်ကြီးသလောက် အများကြီးစဉ်းစားရပါတယ်။ ဒါမျိုးကြုံလာခဲ့ရင်လည်း API version ခွဲထုတ်ပေးတာမျိုးနဲ့ ဖြေရှင်းလို့ရနိုင်ပါလိမ့်မယ်။\n","date":"10 ဇန်နဝါရီ 2025","externalUrl":null,"permalink":"/posts/cursor-pagination/","section":"Posts","summary":"","title":"Cursor Pagination","type":"posts"},{"content":"","date":"10 ဇန်နဝါရီ 2025","externalUrl":null,"permalink":"/tags/mongodb/","section":"Tags","summary":"","title":"Mongodb","type":"tags"},{"content":"","date":"10 ဇန်နဝါရီ 2025","externalUrl":null,"permalink":"/tags/nosql/","section":"Tags","summary":"","title":"Nosql","type":"tags"},{"content":"","date":"10 ဇန်နဝါရီ 2025","externalUrl":null,"permalink":"/tags/postgres/","section":"Tags","summary":"","title":"Postgres","type":"tags"},{"content":"","date":"10 ဇန်နဝါရီ 2025","externalUrl":null,"permalink":"/tags/sql/","section":"Tags","summary":"","title":"Sql","type":"tags"},{"content":"","date":"5 ဇန်နဝါရီ 2025","externalUrl":null,"permalink":"/tags/python/","section":"Tags","summary":"","title":"Python","type":"tags"},{"content":"","date":"5 ဇန်နဝါရီ 2025","externalUrl":null,"permalink":"/tags/regex/","section":"Tags","summary":"","title":"Regex","type":"tags"},{"content":"Read this article in English\nအပိုင်း ၁\nPipe Metacharacter # အပိုင်း ၁ မှာပြောခဲ့တဲ့ .၊ ?၊ +၊ *၊ ^ နဲ့ $ metacharacter တွေအပြင် အခြား အသုံးဝင်တာတခုက | ပါ။\nimport re print(re.findall(r\u0026#39;Go|Python\u0026#39;, \u0026#39;I\\\u0026#39;m intrested in Go, JavaScript, Python, and SQL\u0026#39;)) အခြား programming language တွေမှာလိုပဲ | ကို OR operator အနေနဲ့ မြင်ကြည့်လို့ရပါတယ်။ အပေါ်က Python code မှာဆိုရင် Go ဒါမှမဟုတ် Python လို့ match လုပ်လိုက်တာဆိုတော့ result က ['Go', 'Python'] ဖြစ်မှာပါ။\nCharacter Classes # Square bracket [] ကို regular expression မှာ set နဲ့ range အတွက်သုံးပါတယ်။\nSet ဆိုတာက character၊ digit၊ symbol အစုအဝေးလိုမျိုးပါ။ ဥပမာ [abc] ဆိုရင် lower letter a၊ b၊ c ထဲက တခုခုနဲ့ match လုပ်မှာပါ။ Range ဆိုတာကတော့ ၊ အစုအဝေးမဟုတ်ပဲ နဲ့ လို ပုံစံမျိုးပါ။ ဥပမာ ဆို ကနေ အထိ မယ်၊ ဆိုရင် ကနေ အထိ မယ်ပေါ့။ Set # import re print(re.findall(r\u0026#39;[nl]ot\u0026#39;, \u0026#39;Not not Hot hot Lot lot\u0026#39;)) ဒီ code မှာဆိုရင် n ဒါမှမဟုတ် l နဲ့အစပြုထားရမယ်၊ အနောက်ကနေ ot ကပ်လိုက်ရမယ်လို့ match လုပ်ထားလိုက်တာပါ။ Result အနေနဲ့ ['not', 'lot'] လို့ ရပါလိမ့်မယ်။\n[nl] expression ကို case insensitive ဖြစ်စေချင်တယ်ဆိုရင် အပေါ်က pipe character ကိုသုံးပြီး ဒီလိုရေးလို့ရပါတယ်။\nimport re print(re.findall(r\u0026#39;[N|nL|l]ot\u0026#39;, \u0026#39;Not not Hot hot Lot lot\u0026#39;)) Result အနေနဲ့ ['Not', 'not', 'Lot', 'lot'] ဆိုပြီးရလာပါလိမ့်မယ်။ ဒီနေရာမှာ | မသုံးပဲနဲ့ case ignorecase flag သုံးပြီးလည်း ရေးလို့ရပါတယ်။ Python မှာကတော့ အောက်ကလို parameter အနေနဲ့ ပေးရပါတယ်။\nimport re print(re.findall(r\u0026#39;[nl]ot\u0026#39;, \u0026#39;Not not Hot hot Lot lot\u0026#39;, re.IGNORECASE)) Range # import re if re.fullmatch(r\u0026#39;[A-Za-z0-9]+\u0026#39;, \u0026#39;NoSpaceAndSpecialCharacter0123456789\u0026#39;): print(\u0026#39;Match\u0026#39;) else: print(\u0026#39;Not match\u0026#39;) ဒီလိုမျိုး alphabat နဲ့ number တွေပဲပါရမယ်၊ တခြား space နဲ့ special character တွေမပါရဘူးဆိုတဲ့ regular expression မျိုးကို programmer တော်တော်များများ သုံးဖူးကြမှာပါ။ ဒီမှာဆိုရင် print('Match') ကို run သွားပါလိမ့်မယ်။ တကယ်လို့ space နဲ့ special character တွေ ပါလာမယ်ဆိုရင်တော့ print('Not match') ကို run သွားမှာပါ။\nNot In ^ # ^ ကို start of the string အနေနဲ့ အပိုင်း ၁ မှာ ပြောခဲ့ပါတယ်။ သူ့ကို set နဲ့ range မှာ not in အနေနဲ့လည်း သုံးပါတယ်။\nimport re print(re.findall(r\u0026#39;[^A-Za-z0-9]+\u0026#39;, \u0026#39;NoSpaceAndSpecialCharacter#!0-0 123456789\u0026#39;)) print(re.findall(r\u0026#39;[^nl]ot\u0026#39;, \u0026#39;not hot lot\u0026#39;))k ဒီ code မှာဆိုရင် ပထမ print မှာ alphabatic ရော digit ရောမဟုတ်ရဘူးဆိုတဲ့အတွက် result က ['#!', '-', ' '] ရပါလိမ့်မယ်။\nဒုတိယ print မှာတော့ n နဲ့ရော l နဲ့ရော မစရတဲ့အတွက် result က ['hot'] ဖြစ်မှာပါ။\nGreedy and Non-Greedy Quantifiers # Greedy Quantifier # Standard quantifier တွေဖြစ်တဲ့ .၊ ?၊ +၊ * နဲ့ {from, to| အစရှိသဖြင့်တွေက default အနေနဲ့ greedy ပါ။ Greedy ဆိုတာက match ဖြစ်နေသေးသ၍ ရနိုင်သလောက် match လုပ်တာကို ပြောတာပါ။ အခုပြောခဲ့တာက ရှုပ်နေသလိုဖြစ်ပေမဲ့ အောက်က code ကိုကြည့်လိုက်ရင်ရှင်းပါလိမ့်မယ်။\nimport re print(re.findall(r\u0026#39;\\w+\u0026#39;, \u0026#39;abcdefh123!@#\u0026#39;)) ဒီ code ကို run ကြည့်လိုက်ရင် result က သိထားပြီးဖြစ်တဲ့အတိုင်း ['abcdefh123'] လို့ထွက်မှာပါ။ r'\\d+' လို့ပြောင်းပြီး match လိုက်ရင်တော့ result က ['123'] ဖြစ်မှာပါ။\nဒီနေရာမှာ + quantifier က a ကနေစပြီး match လို့ရသလောက် match သွားတာပြီး ! ကျမှ match မဖြစ်တော့တဲ့အတွက် ဒီ result ရလာတယ်ထင်ရင် အပြည့်မမှန်ပါဘူး။ ဘာဖြစ်လို့လဲဆိုတာ နောက်ဥပမာတခုနဲ့ ဆက်ပြောပြပါမယ်။\nimport re print(re.findall(r\u0026#39;.*hello\u0026#39;, \u0026#39;xhello123\u0026#39;)) ဒီ code မှာဆိုရင် .* က sentence တခုလုံးကို match ဖြစ်နေပြီးတော့ result က ['xhello123'] ရမယ်လို့ ထင်ရမှာပါ။ ဒါပေမဲ့ .* မှာ sentence တခုလုံး match ပြီးမှ ကျန်တဲ့ token တွေကို backtrack နဲ့ တခုစီ process လုပ်သွားတာပါ။ ရှုပ်နေမယ်ဆိုရင် အောက်မှာတဆင့်စီ ပြောပြပေးထားပါတယ်။\n.*: xhello123 .*h: xhello123 -\u0026gt; xhello123 -\u0026gt; xhello123 -\u0026gt; xhello123 -\u0026gt; xhello123 -\u0026gt; xhello123 -\u0026gt; xhello123 -\u0026gt; xhello123 .*hello: xhello ဒီ ဥပမာကို DataCamp ရဲ့ Regular Expression in Python ကနေယူထားတာပါ။ တကယ်လို့ Python နဲ့ data engineering ကို beginner အနေနဲ့ စလေ့လာမယ် ဆိုရင် DataCamp ကို recommend လုပ်ပါတယ်။\nNon-Greedy Quantifier # Non-greedy quantifier ကို lazy quantifier လို့လဲခေါ်ပါတယ်။ သူက greedy နဲ့မတူပဲ အနည်းဆုံးပမာဏကိုပဲ match လုပ်တာမလို့ပါ။\nimport re print(re.findall(r\u0026#39;\\w+?\u0026#39;, \u0026#39;abcdefh123!@#\u0026#39;)) ဒီဥပမာမှာ ထင်ထားတဲ့အတိုင်းပဲ အဖြေက \u0026lt;re.Match object; span=(0, 1), match='a'\u0026gt; ဖြစ်မှာပါ။\nOutroduction # အပိုင်း ၁ မှာ ပြောခဲ့တာကို ထပ်ပြောရရင် ဒီမှာရေးခဲ့တာတွေကို ကျွမ်းကျွမ်းကျင်ကျင်သုံးနိုင်ဖို့က များများလေ့ကျင့်ဖို့လိုပါတယ်။ အခြေခံကျကျနားလည်ထားဖို့ အရေးကြီးသလို လက်တွေ့များများရေးဖူးထားမှ ကျွမ်းကျင်နေမှာပါ။ နောက်အပိုင်းမှာ capturing group နဲ့ backreference အကြောင်း ထပ်ရေးပါမယ်။\nReferences: # Regular Expression in Python by Data Camp regex101.com Set and Ranges by javascript.info ","date":"5 ဇန်နဝါရီ 2025","externalUrl":null,"permalink":"/posts/regular-expressions-101-part-02/","section":"Posts","summary":"","title":"Regular Expressions အခြေခံ (အပိုင်း ၂)","type":"posts"},{"content":"Read this article in English\nအပိုင်း ၂\nIntroduction # ([A-Za-z])\\w+\\s+\\W\\D\nအပေါ်ကလိုမျိုး regular expression ကိုတွေ့ရင် programmer အတော်များများက ဘာအဓိပ္ပါယ်လဲလို့ မသိကြတာများမယ်ထင်တယ်။ ကျွန်တော်လည်းပါတယ်။ ပြီးတော့ validation စစ်ဖို့အတွက် regular expression ရေးရတော့မယ်ဆိုရင် RegExr တို့ regex101 တို့မှာ သွားရေးကြတာ များမလားပဲ။ အခုဆိုရင်တော့ Chat GPT တို့ GitHub Copilot တို့ပေါ့။\nData နဲ့ပတ်သက်တဲ့ course တခု တက်ဖြစ်ရင်းနဲ့ regular expression အကြောင်းပါလာတော့ ကိုယ်တိုင်လည်းမှတ်မိအောင်ဆိုပြီး ဒီ post ကို ရေးထားလိုက်တာပါ။ ဒီ post မှာက Python သုံးပြီး ရေးထားပေမဲ့ regular expression နဲ့ ပတ်သက်တာတွေက တခြား language တွေမှာလည်း အတူတူပါပဲ။\nGeneral Tokens # အခြေခံအနေနဲ့ ဒီ token တွေသိထားရင် pattern တော်တော်များများကို match လုပ်လို့ရတယ်။ တခြား token တွေလည်း ရှိသေးပေမဲ့ အသုံးနည်းလို့ ချန်ထားခဲ့ပါမယ်။\n\\d: 0 to 9 အထိ ဘယ် digit မဆိုအတွက်ပါ။ \\s: space၊ tab နဲ့ new line character တွေအတွက်ပါ။ \\w: ဒါက word character အတွက်ပါ။ a to z၊ A to Z၊ 0 to 9 နဲ့ _ character တွေပါပါမယ်။ .: new line ကလွဲရင် ကျန်တဲ့ တွေ အားလုံးပါပါမယ်။ Token တွေက small letter တွေပဲဖြစ်နေတာကို သတိထားမိမယ်ထင်တယ်။ Capital letter ပြောင်းလိုက်မယ်ဆိုရင် ပြောင်းပြန်ဖြစ်သွားမှာပါ။\n\\D: 0 to 9 အထိ digit ကလွဲရင် ကျန်တဲ့ ဘယ် character မဆိုအတွက်ပါ။ \\S: space၊ tab နဲ့ new line ကလွဲရင် ကျန်တဲ့ ဘယ် character မဆိုအတွက်ပါ။ \\W: a to z၊ A to Z၊ 0 to 9 နဲ့ _ ကလွဲရင် ကျန်တဲ့ ဘယ် character မဆိုအတွက်ပါ။ အောက်က Python code ကို run ကြည့်ရင် result က ['H', 'i', '1', '2', '3'] ဆိုပြီးရပါမယ်။ re.findall() function ရဲ့ ပထမ parameter က regular expression ပါ။\nimport re print(re.findall(r\u0026#39;\\w\u0026#39;, \u0026#39;Hi 123\u0026#39;)) Regular expression ကို r'\\w\\w' လို့ ပြောင်းကြည့်ရင်တော့ ['Hi', '12'] ဆိုပြီး ရမယ်။ Word character နှစ်ခု ဆက်တိုက်ဖြစ်ရမယ်လို့ match လုပ်လိုက်တာမလို့ပါ။ r'\\w\\s\\d' လို့ ပြောင်းကြည့်ရင် ['i 1'] တခုပဲ ရပါမယ်။\nဒီနေရာမှာ တခုမေးဖို့ရှိတာက ဖုန်းနံပါတ်လိုမျိုးမှာ digit ကိုးခု ရှိရမယ်လို့ match လုပ်ချင်ရင် \\d ကို ကိုးခါဆက်တိုက် ရေးရမှာလား၊ character သုံးခုကနေ ငါးခုရှိတာကို match လုပ်ချင်တယ်ဆိုရင်ရော ဘယ်လိုလုပ်ရမှာလဲလို့ စဉ်းစားနိုင်တယ်။ အဲဒီလိုအခြေအနေမျိုးအတွက်က quantifier ကို တွဲပြီးသုံးရမှာပါ။\nGeneral Quantifiers # အောက်က quantifier တွေက အရိုးရှင်းဆုံးနဲ့ အများဆုံးသုံးကြတာတွေပါ။\n?: Zero or one occurrence. *: Zero or more occurrences. +: One or more occurrences. {5}: Exactly five occurrences. {5,9}: Between five and nine occurrences. {5,}: five or more occurrences. import re print(re.findall(r\u0026#39;\\d+\u0026#39;, \u0026#39;Hi 123 4567 89101112\u0026#39;)) ဒီ Python example မှာဆိုရင် digit တခု ဒါမှမဟုတ် တခုထက်ပိုပြီး ဆက်တိုက်လာဖို့ကို match လုပ်လိုက်တာမလို့ ရလာဒ်က ['123', '4567', '89101112'] ဖြစ်မှာပါ။\nတကယ်လို့ r'\\d{4}' နဲ့ match လုပ်ရင်တော့ ရလာဒ်က ['4567', '8910', '1112'] ဖြစ်ပြီး r'\\d{4,}' နဲ့ match လုပ်ရင်တော့ ['4567', '89101112'] ဖြစ်လိမ့်မယ်။\nဒီမှာ တခုသတိထားသင့်တာက quantifier တွေက immediate to the left ပါ။ Quantifier ရဲ့ ဘယ်ဘက်ကပ်လျက်မှာရှိနေတဲ့ character ဒါမှမဟုတ် token ပေါ်မှာပဲ သက်ရောက်ပါလိမ့်မယ်။\nimport re print(re.findall(r\u0026#39;1\\w+\u0026#39;, \u0026#39;1a1b1c1d1e1f1g\u0026#39;)) ဒီ Python code မှာဆိုရင် ရလာဒ်က ['1a', '1b', '1c', '1d', '1e', '1f', '1g'] မဟုတ်ပဲ ['1a1b1c1d1e1f1g'] ဖြစ်မှာပါ။ 1 နဲ့ \\w နှစ်ခုတွဲပေါ်မှာ + quantifier က သက်ရောက်တာမဟုတ်ပဲ 1 အနောက်မှာ တခု ဒါမှမဟုတ် တခုထက်ပိုတဲ့ character တွေလိုက်ရမယ်လို့ match လုပ်ထားတာမလို့ပါ။\nExcaping Special Character # import re print(re.findall(r\u0026#39;.\\s\u0026#39;, \u0026#39;This is first sentence. And this is second sentence.\u0026#39;)) ဒီ Python code က အမှန်တော့ full stop အနောက်မှာ့ space ကပ်ပါလားဆိုတာကို စစ်ချင်တာ။ ဒါပေမဲ့ . ကို any character လို့ ယူဆသွားတဲ့အတွက် ရလာဒ်က ['s ', 's ', 't ', '. ', 'd ', 's ', 's ', 'd '] ဖြစ်နေပါလိမ့်မယ်။ Special character ဖြစ်တဲ့ . ကို escape လုပ်ဖို့ \\ ကိုသုံးပြီး r'\\.\\s' လို့ပြင်လိုက်ရင်တော့ ['. '] ရလာဒ်ရလာမှာပါ။\nLast But Not Least # နောက်ထပ်အသုံးဝင်တဲ့ token နှစ်ခုက ^ နဲ့ $ ပါ။\n^: Matches the start of the string. $: Matches the end of the string. import re print(re.findall(r\u0026#39;hello_\\d+\u0026#39;, \u0026#39;hello_world hello_123\u0026#39;)) ဒီ Python code မှာဆိုရင် ရလာဒ်က ['hello_123'] ဖြစ်လိမ့်မယ်။ တကယ်လို့ hello_' နဲ့ အစပြုထားပြီး အနောက်ကနေ တခုထက်ပိုတဲ့ digit လိုက်လားဆိုတာမျိုးကို စစ်ချင်တာဆိုရင် r'^hello_\\d+' ကို သုံးမှမှန်ပါလိမ့်မယ်။\nအဲဒီလိုပဲ hello_' အနောက်ကနေ တခုထက်ပိုတဲ့ digit နဲ့ အဆုံးသတ်ထားလား စစ်ချင်ရင်တော့ r'hello_\\d+$' ကို သုံးရမှာပါ။\nPython Functions # ဒီ post က regular expression ကိုပဲ အဓိကပြောချင်တာဖြစ်လို့ re.findall() function ကိုပဲသုံးထားလိုက်တာပါ။ အောက်က Python function တွေကိုလဲ လိုအပ်ချက်ပေါ်မူတည်ပြီး စမ်းသုံးကြည့်ပါ။\nre.match(): Checks for a match only at the start of the string. re.search(): Searches the entire string for the first match. re.sub(): Replaces occurrences of a pattern with a specified string. re.split(): Splits the string at each match of the pattern. Outroduction # Regular Expression အခြေခံ ဆိုတဲ့အတိုင်း ဒီမှာရေးခဲ့တာတွေကို ကျွမ်းကျွမ်းကျင်ကျင်သုံးနိုင်ဖို့က များများလေ့ကျင့်ဖို့လိုပါတယ်။ Email နဲ့ ID number လိုမျိုးတွေကို များများစမ်းပြီး match လုပ်ကြည့်ပါ။ နောက် post တွေကျရင် password validation လိုမျိုး ပိုပြီးရှုပ်ထွေးတဲ့ pattern တွေကို match လုပ်နိုင်ဖို့အတွက် ရေးပါဦးမယ်။\nReferences: # Regular Expression in Python by Data Camp regex101.com ","date":"29 ဒီဇင်ဘာ 2024","externalUrl":null,"permalink":"/posts/regular-expressions-101/","section":"Posts","summary":"","title":"Regular Expressions အခြေခံ","type":"posts"},{"content":"","date":"6 နိုဝင်ဘာ 2024","externalUrl":null,"permalink":"/tags/blowfish/","section":"Tags","summary":"","title":"Blowfish","type":"tags"},{"content":"","date":"6 နိုဝင်ဘာ 2024","externalUrl":null,"permalink":"/tags/hugo/","section":"Tags","summary":"","title":"Hugo","type":"tags"},{"content":"Read this article in English\nWhat is Hugo? # Hugo က ဘာလဲဆိုတဲ့ အကျဉ်းချုပ်ကို Hugo official documentation ထဲကနေပဲ အလွယ်တကူယူသုံးလိုက်ပါတယ်။\nHugo is a static site generator written in Go, optimized for speed and designed for flexibility. With its advanced templating system and fast asset pipelines, Hugo renders a complete site in seconds, often less.\nHugo ကို အခုမှစကြားဖူးတာဆိုရင် Fireship ရဲ့ Hugo in 100 Seconds video လေးကို အရင်ကြည့်စေချင်ပါတယ်။ Why Hugo? # Hugo offers a variety of powerful features, including support for multilingual, templates, rich content formats, easy-to-use shortcodes, and image processing.\nကျွန်တော်အရင်က Medium မှာ article လေး နဲနဲပါးပါးရေးဖူးပေမယ့် blog ကို အခုမှစရေးဖြစ်တာ။ စရေးဖြစ်တဲ့အကြောင်းကလည်း ကိုယ်တွေ့ကြုံဖြေရှင်းခဲ့ဖူးတာတွေကို အချိန်နဲနဲကြာလာတာနဲ့ မမှတ်မိတော့တာကြောင့်ကအဓိကပါ။ Blog စလုပ်မယ်ဆိုတော့ SSG တွေကလည်း တခုမှမသုံးဖူးတာနဲ့ နည်းနည်းပါးပါးလိုက်ရှာပြီး Hugo ကိုပဲ သုံးဖြစ်လိုက်တယ်။ အဓိကက ကိုယ်က Go နဲ့ ပိုရင်းနီးတော့ Go\u0026rsquo;s text/template နဲ့ html/template လိုမျိုးတွေ သုံးဖို့လိုလာရင် ပိုရင်းနီးတာကြောင့်ရယ်၊ မြန်မာလို ရော English လိုရောကို အဆင်ပြေပြေပြောင်းလို့ရတာတွေကြောင့်လို့ ပြောရမယ်။\nWhat is Blowfish? # Theme အတွက်က Blowfish ကို သုံးဖြစ်လိုက်တယ်။ Automatic image resizing၊ integrated site search နဲ့ documentation ကောင်းလို့ပါ။ Documentation အသေးစိတ်ကို ဒီမှာ ကြည့်လို့ရတယ်။ သူက Tailwind CSS v3 နဲ့။\nSetting Hugo on Local # Prerequisites # Hugo ကို local မှာ run ဖို့ဆိုရင် Git နဲ့ Go လိုမယ်။ Dart Sass ကတော့ သာမန် blog အတွက်မလိုပါဘူး။\nGit ကို Hugo Modules သုံးမယ်ဆိုရင်၊ theme ကို Git Submodule နဲ့ သွင်းမယ်ဆိုရင်နဲ့ blog ကို GitHub Pages ပေါ်မှာတင်မယ်ဆိုရင် မဖြစ်မနေလိုမယ်။\nInstallation # Installation ကို Homebrew နဲ့ပဲ လုပ်လိုက်တယ်။\nbrew install hugo တကယ်လို့ Homebrew မရှိသေးရင် အောက်က script နဲ့ အရင် install လုပ်ဖို့လိုမယ်။\n/bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\u0026#34; Setting Up # အရင်ဆုံး Hugo project တခု set up လုပ်မယ်။ နာမည်ကတော့ personal-blog လို့ပဲပေးလိုက်မယ်။ hugo new site personal-blog cd personal-blog hugo mod init github.com/pyaethu-aung/personal-blog hugo mod tidy Theme အနေနဲ့ Blowfish ကို သုံးဖို့အတွက် configuration files တွေကို ဒီ link ကနေ လုပ်ပီး /config/_default directory ထဲမှာထည့်ပေးရမယ်။ ပြီးရင် Blowfish ကို Hugo module အနေနဲ့ သုံးဖို့အတွက် အောက်က configuration ကို config/_default/module.toml file မှာထည့်ပေး။ [[imports]] disable = false path = \u0026#34;github.com/nunocoracao/blowfish/v2\u0026#34; Theme လည်း set up လုပ်ပြီးပြီဆိုရင် အောက်က command နဲ့ localhost 8080 မှာ blog ကို ကြည့်လို့ရပြီ။ hugo server --port 8080 Home page after initial set up Configuration # /config/_default/languages.en.toml မှာ အောက်ကအတိုင်း blog ရဲ့ ခေါင်းစဉ်၊ ကိုယ့်နာမည်၊ email၊ GitHub link စတာတွေကို configure လုပ်ပေး။ title = \u0026#34;My Personal Blog\u0026#34; [params.author] name = \u0026#34;My Name\u0026#34; email = \u0026#34;hello@myname.com\u0026#34; headline = \u0026#34;I\u0026#39;m writing blog by using Hugo and Blowfish\u0026#34; bio = \u0026#34;Freelance Developer\u0026#34; links = [ { email = \u0026#34;mailto:hello@myname.com\u0026#34; }, { github = \u0026#34;https://github.com/myname\u0026#34; } ] File ကို save လိုက်ရင် home page မှာ ကိုယ်ပြင်လိုက်တဲ့အတိုင်း hot reload နဲ့ မြင်ရမယ်။ Home page after changing languages.en.toml Menu အတွက်က /config/_default/menus.en.toml မှာ posts နဲ့ tags configuration တွေကို uncomment လုပ်ပေးလိုက်ရုံပဲ။ Blowfish က post တင်တာနဲ့ အလိုလိုထည့်ပေးသွားပါလိမ့်မယ်။ [[main]] name = \u0026#34;Blog\u0026#34; pageRef = \u0026#34;posts\u0026#34; weight = 10 [[main]] name = \u0026#34;Tags\u0026#34; pageRef = \u0026#34;tags\u0026#34; weight = 30 Home page after changing menus.en.toml ကျွန်တော်က home page ကို hero layout ထားချင်တာ။ ပြီးတော့ နောက်ဆုံးတင်ခဲ့တဲ့ post တွေကိုလည်း တခါတည်းမြင်ချင်တာဆိုတော့ /config/_default/params.toml မှာ အောက်က configuration အတိုင်း ပြောင်းလိုက်ပါတယ်။ ဒီမှာဆိုရင် hero_background.jpg file က /assets/images/ ထဲမှာရှိနေဖို့တော့လိုမယ်။ [homepage] layout = \u0026#34;hero\u0026#34; homepageImage = \u0026#34;images/hero_background.jpg\u0026#34; showRecent = true showMoreLink = true cardView = true Home page after changing params.toml Add a New Post # Markdown file နဲ့ post အသစ်တခု စလုပ်လိုက်မယ်။ Markdown မဟုတ်ပဲ HTML နဲ့ Pandoc လို format တွေနဲ့လည်းရတယ်။ ဒီ link မှာ အသေးစိတ်ကြည့်လို့ရပါတယ်။ hugo new content content/posts/my-first-post/index.md index.md ရဲ့ front matter ကို အောက်ကအတိုင်း ထည့်လိုက်မယ်။ ဒီမှာထည့်ထားတဲ့ tag တွေကို tags menu ထဲမှာ စမြင်ရပါလိမ့်မယ်။ Sample content လိုချင်ရင် Lorem Markdownum မှာသွားလုပ်လို့ရပါတယ်။ +++ title = \u0026#34;My First Post\u0026#34; date = 2024-11-06T10:10:00+00:00 draft = false tags = [\u0026#34;hugo\u0026#34;, \u0026#34;blowfish\u0026#34;] slug = \u0026#34;my-first-post\u0026#34; +++ Post မှာ author information ပြဖို့အတွက်က /config/_default/params.toml ထဲမှာ article အောက်က showAuthor ကို true လို့ ပြောင်းပေးရုံပါပဲ။ [article] showAuthor = true Home page after adding a new post New post Post ထဲမှာ hero section ထည့်ဖို့နဲ့ author information ကို post အောက်မှာ ပြဖို့အတွက်က /config/_default/params.toml မှာ အောက်ကအတိုင်း ပြောင်းပေးရပါမယ်။ Hero image featured.jpeg ကို index.md file နဲ့ folder အတူတူထားပေးဖို့လိုပါမယ်။ [article] showAuthorBottom = true showHero = true heroStyle = \u0026#34;big\u0026#34; New post with hero image Home list item with hero image အပေါ်က အဆင့်ဆင့်လုပ်လာခဲ့ပြီးတဲ့ project ကို ဒီ link မှာ download လုပ်လို့ရပါတယ်။\nနောက်ပိုင်းမှ blog ကို GitHub Pages မှာ host လုပ်တာနဲ့ post တွေကို Hugo ရဲ့ multilingual mode နဲ့ English မြန်မာ နှစ်မျိုးတင်တာကို ထပ်ရေးပါဦးမယ်။\n","date":"6 နိုဝင်ဘာ 2024","externalUrl":null,"permalink":"/posts/personal-blog-using-hugo-and-blowfish/","section":"Posts","summary":"","title":"Hugo၊ Blowfish နဲ့ Personal Blog","type":"posts"},{"content":"","externalUrl":null,"permalink":"/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","externalUrl":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"}]