Code that tell a story
วันนี้ผมได้รีวิวโค้ดของน้องคนนึง ซึ่งเขาต้องการแก้โค้ดประมาณนี้
function doSomethingA() {
// ............ Some statements step2() // ............ Another many statements
}
เขาต้องแก้สร้าง doSomethingB ซึ่งยังทำทุกอย่างเหมือนเดิม เปลี่ยนแค่ step2
Intuition แรกที่คิดขึ้นมาคือ ในเมื่อ มันต่างกันแค่ Step 2 เราก็ Inject step 2 สิ
function createDoSomething({ step2Injected }) {
// ............ Some statements step2Injected() // ............ Another many statements
}function doSomethingA() {
const doSomething = createDoSomething({ step2Injected = step2 })
return doSomething()
}function doSomethingB() {
const doSomething = createDoSomething({
step2Injected = step2Special
})
return doSomething()
}
ซึ่งมันก็ตรงไปมาแหละ
พอผมอ่าน ผมบอกว่า อ่านยากนะ คือในทางทฤษฎีมันก็ถูกว่าทุกอย่างเหมือนกัน ต่างกันแค่นี้ เราก็ If แล้ว Inject มาสิ
แต่ มันเสีย “ความต่อเนื่องของเรื่องราว” ไป
ผมแนะนำให้
- ตั้งชื่อตั้งความหมายให้สิ่งที่ทำก่อน Step2
- ตั้งชื่อตั้งความหมายให้สิ่งที่ทำหลัง Step2
- เล่าเรื่องราวของมันเป็นเรื่องสั้นๆ 3 บรรทัด
ผลออกมาแบบนี้
function step1 () {
... // Refactor from original doSomethingA
}function step3 () {
... // Refactor from original doSomethingA
}function doSomethingA () {
step1()
step2()
step3()
}function doSomethingB () {
step1()
step2Special()
step3()
}
พอเรา Refactor จน doSomethingA, doSomethingB มันเหลือแค่ 3 บรรทัดแล้วแบบนี้ เราเห็นชัดเจนว่ามันต่างกันที่นึง ในระยะ 6 บรรทัด จากเดิมที่เราต้อง Navigate ไประหว่างชุดโค้ดยาวๆ (ในชุดโค้ดแรก createDoSomething มันมีความยาวประมาณ 40 บรรทัด)
ถึงแม้ว่าถ้าดู Requirement และสิ่งที่จะทำคือ “ทำทุกอย่างเหมือนเดิมยกเว้นขั้นตอนที่ 2” การแก้ไขที่ตรงไปตรงมาที่สุดคือ Inject ขั้นที่ 2 เข้าไปผ่าน Dependency injection ก็ตาม
ผมมองว่าแบบหลังอ่านง่ายกว่าและแก้ง่ายกว่า
Moral of story
- ระวังเรื่องการยึดติดกับ Status quo คือถ้าเรามองว่าเราอยากทำ Requirement ใหม่โดยไม่ Refactor ของเดิม หรือ Refactor ให้น้อยที่สุด ไม่อยากไปทำลายของเก่า เราจะมองไม่เห็นว่ามันสามารถ Breakdown ได้
- โค้ดที่เล่าเรื่องราวได้ใน 3–4 บรรทัด ถึงแม้จะไม่ Systemmatically Precise ว่า “ต่างกันแค่นี้” แต่ก็จะสบายตาและเข้าใจง่ายกว่า ส่งผลให้แก้ไขง่ายกว่า มีโปรแกรมเมอร์อยากแก้เยอะกว่า (ซึ่งทั้งหมดที่เราคุยเรื่อง Software architecturing คือเราต้องการให้โค้ดแก้ง่าย)
- เมื่อใช้ท่ายากทุกครั้งต้องมองเห็นว่ามันสร้าง Learning curve ดังนั้นมอง Trade-off ดีๆ ถ้ามีสองสิ่งที่มีคุณค่าพอๆ กัน ผมมักจะสนับสนุนให้ Prefer code ที่อ่านได้ง่ายและทำความเข้าใจได้ตั้งแต่ Beginner มากกว่าโค้ดที่ยาก