Clean Code ဆိုတဲ့ စကားလုံးကို Developer တိုင်း ကြားဖူးကြမှာပါ။ ဒါပေမယ့် ရိုးရိုးသားသား ပြောရရင် တော်တော်များများက သူ့ရဲ့ အဓိပ္ပါယ်ကို နည်းနည်း လွဲနေတတ်ကြတယ်။
တချို့ကတော့ ဒီလိုတွေ လုပ်နေရင်း "ငါ Clean Code ရေးနေပြီ" လို့ ထင်နေကြတယ်။
- Indentation ညီရမယ်
- Comment လှလှ ရေးရမယ်
- Variable name ကောင်းရမယ်
ဒါတွေ မှားတာ မဟုတ်ပါဘူး။ ဒါပေမယ့် ဒါတွေက Clean Code ရဲ့ cosmetic (အပြင်အဆင်) ပိုင်းပဲ ရှိသေးတယ်။
တကယ့် Clean Code ဆိုတာ ပြောင်းလဲရ လွယ်တဲ့ maintainable code ကို ပြောတာပါ။ နောက်လူ ဝင်ကြည့်ရင် နားလည်ရမယ်။ Feature အသစ်ထည့်ရင် မကြောက်ရဘူး။ Bug တစ်ခု fix ရင်း feature သုံးခု မပျက်သွားရဘူး။ ပြီးတော့ မနက် ၂ နာရီ Production Error တက်လို့ ဖုန်းဆက်ခံရတဲ့အခါ "ဒီ function က ဘာလုပ်တာလဲ" လို့ ကိုယ့်ကုဒ်ကို ကိုယ်ပြန်မေးနေရတဲ့ အခြေအနေမျိုး မဖြစ်ရဘူး။
Code Review ထဲက အကြီးဆုံး အထင်အမြင်လွဲမှု
Code Review လုပ်တဲ့အခါ ဒီလို comment မျိုး အများကြီး တွေ့ရတယ်။
"Variable name ကို
dataကနေpayloadပြောင်းပါ။"
ဒါပေမယ့် အဲဒီ variable ရှိနေတဲ့ function ကြီးက ၄၀၀ lines ရှည်နေပြီး၊ Database လည်း ချိတ်တယ်၊ API လည်း ခေါ်တယ်၊ Business Logic လည်း လုပ်တယ်၊ Response လည်း return ပြန်တယ် ဆိုရင်တော့ variable name က ပြဿနာ အကြီးဆုံး မဟုတ်တော့ဘူး။
အဲဒါက မီးဖိုချောင် တစ်ခုလုံး မီးလောင်နေတဲ့အချိန်မှာ "လက်အရင်ဆေးပြီးမှ မီးငြိမ်းပါ" လို့ ပြောနေတာနဲ့ တူတယ်။ လက်ဆေးတာ ကောင်းပါတယ်။ ဒါပေမယ့် မီးက အရင် ငြိမ်းဖို့ လိုတယ်။
AI ခေတ်မှာ Code ပိုမြန်လာတယ်၊ ဒါပေမယ့်…
အခုခေတ်မှာ Developer အများစုက ChatGPT, Copilot, Claude လို AI tools တွေ နေ့စဉ် သုံးနေကြပြီ။ Code ထုတ်ရတာ မြန်တယ်။ Feature ဆောက်ရတာ မြန်တယ်။ ဒါပေမယ့် ပြဿနာ တစ်ခု ရှိလာတယ်။
AI ဟာ "code quality ကို ဂရုမစိုက်တတ်" တာ မဟုတ်ဘူး။ ပိုတိကျအောင် ပြောရရင် AI ဟာ သူ့ရဲ့ context window (မြင်နိုင်တဲ့ ကုဒ် အကန့်အသတ်) ထဲမှာ "အလုပ်ဖြစ်မယ့် working code" ကိုသာ optimize လုပ်တာ ဖြစ်တယ်။ တခြားဖိုင်တစ်ခုမှာ ရှိပြီးသား abstraction (function/class) ကို သူ မမြင်ရတော့ ပြန်သုံး (reuse) ဖို့ မသိဘဲ ထပ်ရေးပေးလိုက်တယ်။ Tab တစ်ချက် နှိပ်တိုင်း code block အသစ် ထွက်လာတယ်။ တူတဲ့ logic ကို နေရာ ၅ နေရာမှာ ပြန့်ကျဲသွားတာပါ။
ဒါက ခံစားချက်နဲ့ ပြောနေတာ မဟုတ်ဘူး၊ data ရှိတယ်။ GitClear ရဲ့ ၂၀၂၅ AI Code Quality report (code line ၂၁၁ သန်းကို ၂၀၂၀ ကနေ ၂၀၂၄ အထိ လေ့လာထားတာ) အရ refactoring (code reuse) ဟာ ၂၀၂၁ က ၂၅% ကနေ ၂၀၂၄ မှာ ၁၀% အောက် ကျသွားပြီး၊ duplicated code block တွေက ၂၀၂၄ မှာ ၈ ဆ နီးပါး တိုးလာတယ်။ သမိုင်းမှာ ပထမဆုံးအကြိမ်အဖြစ် copy/paste လုပ်တဲ့ code က refactor လုပ်တဲ့ code ကို ကျော်သွားတဲ့ နှစ်လည်း ဖြစ်ပါတယ်။ Developer က review မလုပ်ရင် Technical Debt တက်တာ အရမ်းမြန်တယ် ဆိုတာ ဒါပါ။
Duplication နဲ့ Rule of Three
AI generated code မှာ အတွေ့ရ အများဆုံး ပြဿနာကို ကြည့်ရအောင်။
function getActiveUsers(users) {
return users.filter(u => u.status === "active" && u.age >= 18);
}
function getActiveAdmins(admins) {
return admins.filter(a => a.status === "active" && a.age >= 18);
}
function getActiveGuests(guests) {
return guests.filter(g => g.status === "active" && g.age >= 18);
}
အလုပ်လုပ်တယ်။ Bug မရှိဘူး။ ဒါပေမယ့် business rule တစ်ခုတည်းကို နေရာ ၃ နေရာမှာ copy လုပ်ထားတယ်။ မနက်ဖြန် "18 → 21" ပြောင်းရင်ကော။ Function သုံးခုလုံး ပြင်ရမယ်။ တစ်ခု မေ့သွားရင် bug တက်မယ်။
ဒီနေရာမှာ ကောင်းကောင်း ရေးချင်ရင် TypeScript နဲ့ generic သုံးလိုက်ရင် type safety လည်း မပျက်၊ DRY လည်း ရတယ်။
type Person = { status: string; age: number };
const isActiveAdult = (p: Person): boolean =>
p.status === "active" && p.age >= 18;
// <T extends Person> က User / Admin / Guest ဆိုတဲ့ concrete type ကို မပျောက်စေဘူး
const getActive = <T extends Person>(people: T[]): T[] =>
people.filter(isActiveAdult);
const activeUsers = getActive(users); // User[] ပြန်ရတယ်
const activeAdmins = getActive(admins); // Admin[] ပြန်ရတယ်
Business rule က isActiveAdult တစ်နေရာတည်းမှာ ရှိတယ်။ ပြောင်းချင်ရင် တစ်ကြောင်းပဲ ပြင်ရတယ်။ <T extends Person> ကြောင့် getActive(users) က User[]၊ getActive(admins) က Admin[] အဖြစ် မှန်မှန်ကန်ကန် ပြန်ရနေသေးတယ်။
တူတဲ့ logic ကို ၃ ကြိမ် တွေ့ရင် refactor လုပ်ဖို့ စဉ်းစားပါ။ ဒါကို Rule of Three လို့ ခေါ်တယ်။
- ပထမအကြိမ်မှာ Copy (ရှက်စရာ မဟုတ်ဘူး)
- ဒုတိယအကြိမ်မှာ သတိထား
- တတိယအကြိမ်မှာ Refactor
ဒါပေမယ့် DRY ကို Over-engineer မလုပ်ပါနဲ့
ဒီနေရာမှာ senior caveat တစ်ခု ထည့်ချင်တယ်။ Duplication တွေ့တိုင်း ချက်ချင်း abstraction ထုတ်ဖို့ မလိုပါဘူး။ Sandi Metz ရဲ့ နာမည်ကြီး Quote တစ်ခု ရှိတယ်။
"Wrong abstraction က duplication အနည်းငယ် ရှိနေတာထက် ပိုပြီး တန်ဖိုးကြီးတယ် (ပိုခေါင်းကိုက်ရတယ်)။"
ဘာလို့လဲဆိုတော့ duplication ကို ဖြေရှင်းရတာက copy/paste လုပ်ရုံ လွယ်ပေမယ့်၊ မှားနေတဲ့ abstraction ကို ပြန်ဖြုတ် (un-abstract) ရတာက ပိုခက်လို့ပါ။ Layer တစ်ခု ထပ်တိုင်းဟာ နောက်လူ နားလည်ရမယ့် ဝန်ထုပ်တစ်ခုလည်း ဖြစ်တယ်။ ဒါကြောင့် "တစ်ခါ copy ရင် အပြစ်မရှိ၊ ဒုတိယ သတိထား၊ တတိယ ကျမှ extract" ဆိုတဲ့ Rule of Three က DRY ရော over-engineering ရော နှစ်ဖက်စလုံးကနေ ကာကွယ်ပေးတာပါ။
Flutter: God Widget vs Clean Architecture
Flutter Developer တွေ၊ အထူးသဖြင့် beginner တွေမှာ အတွေ့ရ အများဆုံးက widget တစ်ခုတည်းထဲမှာ API call, state, error handling, business logic, UI အကုန် ရောထည့်ထားတာပါ။ အစမှာ အဆင်ပြေပေမယ့် project ကြီးလာတာနဲ့ ဘာက ဘယ်နေရာမှာ ရှိမှန်း ဘယ်သူမှ မသိတော့ဘူး။
// God Widget: UI + API + state + error အကုန် တစ်နေရာတည်း (ဒါ မလုပ်သင့်ဘူး)
class _ProductPageState extends State<ProductPage> {
List<Product> products = [];
bool isLoading = false;
String? error;
Future<void> _load() async {
setState(() => isLoading = true);
try {
final res = await http.get(Uri.parse('https://api.shop.com/products'));
final data = jsonDecode(res.body) as List;
setState(() {
products = data.map((j) => Product.fromJson(j)).toList();
isLoading = false;
});
} catch (e) {
setState(() { error = e.toString(); isLoading = false; });
}
}
@override
Widget build(BuildContext context) {
if (isLoading) return const CircularProgressIndicator();
if (error != null) return Text('Error: $error');
return ListView(children: products.map((p) => Text(p.name)).toList());
}
}
ဒီနေရာမှာ Provider, Riverpod, Bloc စတာတွေ ပေါ်လာတာ မှန်ပေမယ့်၊ သူတို့ရဲ့ မူလ ရည်ရွယ်ချက်က widget tree တစ်လျှောက် state ကို မျှဝေဖို့ပါ။ တကယ့် ပြဿနာရင်းကို ဖြေရှင်းတာက library ရွေးတာ မဟုတ်ဘဲ Responsibility ခွဲတာပါ။ UI က UI ပဲ သိရမယ်။ Business logic က logic ပဲ သိရမယ်။ Repository က data fetching ပဲ သိရမယ်။
// 1. Repository: data fetching ပဲ သိတယ်
class ProductRepository {
final http.Client client;
ProductRepository(this.client);
Future<List<Product>> fetchProducts() async {
final res = await client.get(Uri.parse('https://api.shop.com/products'));
final data = jsonDecode(res.body) as List;
return data.map((j) => Product.fromJson(j)).toList();
}
}
// 2. Controller: state + logic ပဲ သိတယ်
class ProductController extends ChangeNotifier {
final ProductRepository repo;
ProductController(this.repo);
bool isLoading = false;
String? error;
List<Product> products = [];
Future<void> load() async {
isLoading = true;
error = null;
notifyListeners();
try {
products = await repo.fetchProducts();
} catch (e) {
error = e.toString();
} finally {
isLoading = false;
notifyListeners();
}
}
}
// 3. UI: render ပဲ သိတယ်
class ProductPage extends StatelessWidget {
final ProductController controller;
const ProductPage({super.key, required this.controller});
@override
Widget build(BuildContext context) {
return ListenableBuilder(
listenable: controller,
builder: (context, _) {
if (controller.isLoading) return const CircularProgressIndicator();
if (controller.error != null) return Text('Error: ${controller.error}');
return ListView(
children: controller.products.map((p) => Text(p.name)).toList(),
);
},
);
}
}
အလုပ်ချင်း တူပေမယ့်၊ အခု အပိုင်းတိုင်း သူ့တာဝန် သူ သိတယ်။ API endpoint ပြောင်းချင်ရင် ProductRepository ပဲ ထိရတယ်။ UI ပြောင်းချင်ရင် ProductPage ပဲ ထိရတယ်။ State logic မှားရင် ProductController ကိုပဲ ကြည့်ရတယ်။
Clean Code ရဲ့ တကယ့် Superpower က Testability
Responsibility ခွဲတဲ့ အကောင်းဆုံး အကျိုးကျေးဇူးက "ကြည့်ရ ရှင်းတာ" မဟုတ်ဘူး။ Test ရေးလို့ ရလာတာ ဖြစ်တယ်။
အပေါ်က God Widget ကို unit test ဘယ်လို ရေးမလဲ။ မရေးနိုင်ဘူး။ Logic က UI ထဲ ကပ်နေတာမို့ http အစစ်ကို ခေါ်မိမယ်၊ widget တစ်ခုလုံး pump လုပ်မှ test ရမယ်၊ network down ရင် test က ပျက်မယ်။ အဲဒါ unit test မဟုတ်တော့ဘူး။
ခွဲထားတဲ့ version မှာတော့ ProductController ကို fake repository တစ်ခုနဲ့ test လို့ ရတယ်။ http မလို၊ UI မလို၊ စက္ကန့်ပိုင်းနဲ့ ပြီးတယ်။
test('load() success ဆို products ပြည့်ပြီး error null ဖြစ်ရမယ်', () async {
final controller = ProductController(FakeProductRepository());
await controller.load();
expect(controller.products, isNotEmpty);
expect(controller.error, isNull);
expect(controller.isLoading, isFalse);
});
400-line function တွေ၊ god widget တွေက test မရေးနိုင်လို့ untestable ဖြစ်နေတာ ကုဒ် ညံ့လို့ မဟုတ်ဘဲ၊ တာဝန်တွေ ရောနေလို့ပါ။ Test ရေးလို့ ရတဲ့ code က definition အရ Clean Code ပါ။
Clean Code Checklist
ကျွန်တော် အမြဲ ကြည့်တဲ့ အချက် ၃ ခု ရှိတယ်။
၁။ Naming. a, temp, data ထက် userAge, isLoggedIn, totalPrice က ပိုကောင်းတယ်။ ဖတ်တဲ့သူ guess လုပ်စရာ မလိုတော့ဘူး။ Name က documentation ပါ။
၂။ Separation of Concerns. Function တစ်ခုက email ပို့၊ DB update၊ API call၊ logging အကုန် လုပ်နေရင် ပြဿနာ ရှိတယ်။ Function/class တစ်ခုဟာ အဓိက တာဝန် တစ်ခုပဲ ရှိသင့်တယ်။
၃။ Tooling. Formatting ကို ကိုယ်တိုင် မလုပ်ပါနဲ့။ Tool ကို လုပ်ခိုင်းပါ။ ပြီးတော့ auto-fix flag တွေ သုံးပါ။
# Python (Ruff: Rust နဲ့ ရေးထား၊ flake8/black/isort အစား)
ruff check --fix . # lint + auto-fix
ruff format .
# JavaScript / TypeScript (Biome: eslint + prettier အစား)
biome check --write . # lint + format + import sort တစ်ခါတည်း
# Flutter / Dart
dart format .
dart fix --apply
flutter analyze
Formatting ကို tool လုပ်ခိုင်းပြီး၊ architecture ကို Developer က စဉ်းစားပါ။
Code Review မှာ ဘာကို ကြည့်သင့်လဲ
"Add trailing comma"၊ "Rename temp variable" လို comment တွေက formatter က လုပ်ပြီးသား ဖြစ်သင့်တယ်။ Human တစ်ယောက်ရဲ့ အချိန်ဟာ ဒီထက် ပိုတန်ဖိုးရှိတဲ့ မေးခွန်းတွေအတွက် ဖြစ်သင့်တယ်။
- ဒီ logic က ရှိပြီးသား မဟုတ်လား (
utilsထဲ ရှိနေပြီလား) - ဒါကို Repository ထဲ ပြောင်းထည့်လို့ ရမလား
- UI နဲ့ business logic ခွဲရင် ပိုကောင်းမလား
- ဒီ function က responsibility များနေပြီလား
Robot ကို hygiene လုပ်ခိုင်းပြီး၊ human ကို judgment လုပ်ခိုင်းပါ။ Clean Code ရဲ့ တကယ့်တန်ဖိုးက ဒီနေရာမှာ ပေါ်လာတာပါ။
အဆုံးသတ်
Clean Code ဆိုတာ ကြည့်လို့ လှဖို့ မဟုတ်ဘူး။ ပြောင်းလဲရ လွယ်ဖို့ ဖြစ်တယ်။ AI tools တွေက code ကို ပိုမြန်စေတယ်။ ဒါပေမယ့် duplication တွေ၊ technical debt တွေကိုလည်း တပြိုင်နက် ပိုမြန်စေတယ်။ ဒါကြောင့်
- Naming ကောင်းကောင်း ပေးပါ
- DRY ကို လိုက်နာပါ၊ ဒါပေမယ့် Over-engineer မလုပ်ပါနဲ့ (Rule of Three)
- Responsibility ခွဲရေးပါ (testable ဖြစ်အောင်)
- Tool တွေကို formatting လုပ်ခိုင်းပါ
- Architecture ကို ကိုယ်တိုင် အာရုံစိုက်ပါ
နောက်ဆုံးမှာတော့ Clean Code ဆိုတာ "ဒီ code ကို နောက် ၆ လကြာပြီး ပြန်ကြည့်တဲ့အခါ ကိုယ့်ကိုယ်ကိုယ် မဆဲမိတဲ့ code" ပါ။
Clean Code ရဲ့ တကယ့်တန်ဖိုးက ကုဒ် ကြည့်ရ လှသွားတာ မဟုတ်ဘဲ၊ team တစ်ခုလုံး မြန်မြန် ship လုပ်နိုင်ပြီး၊ feature အသစ်တွေကို စိတ်ချလက်ချ ထည့်နိုင်ကာ၊ business ကို တကယ့်တန်ဖိုး ပြန်ပေးနိုင်တဲ့ နေရာမှာ ရှိပါတယ်။