มาเขียน Specification กันเถอะ…

จากบล็อคที่แล้ว Test-Driven Development (TDD): คิดอย่างชัดเจนเหมือนนักวิทยาศาสตร์, ทำอย่างประณีตเหมือนช่างไม้  เป็นแนวคิดที่แนะนำให้โปรแกรมเมอร์เขียน spec ก่อนเขียน code

การเขียน spec ไม่ใช่การทดสอบ หรือ การทำ automated test หรือ มาใช้แทนรูปแบบการเขียนความต้องการใดๆทั้งสิ้น มันก็แค่การเขียนเอกสารชนิดหนึ่ง ที่มีจุดประสงค์เพื่อใช้อธิบายปัญหา ให้เข้าใจได้ด้วยการอ่านด้วยคนได้ทันที และ มีขั้นตอนวิธีแก้ปัญหาที่แสดงเป็น algorithm ที่สามารถตรวจสอบความถูกต้องได้ ก่อนที่เราจะเขียน code หรือ โปรแกรมขึ้นมาก่อน หรือที่เรียกหลักการนี้ว่า “Test-First” (ถ้าเทียบใน TDD จะอยู่ในขั้นตอนที่เรียกว่า Red step)

การเขียน spec มันก็เหมือนกับที่นักวิทยาศาสตร์เขาใช้โมเดลคณิตศาสตร์ในการอธิบายความเป็นไปของสรรพสิ่งในโลกของเรา แล้วสามารถพิสูจน์ได้ก่อนนำโมเดลนั้นไปสู่หนทางแก้ไขปัญหาได้จริง หรือเป็นทษฏีเพื่อการประฏิบัติต่อไป ซึ่งเราจะเห็นว่าพวกเขาไม่มีคอมไพลเลอร์, xUnit, Automated Test หรือ เครื่องมือช่วยอื่นๆเหมือนพวกเราโปรแกรมเมอร์เลย แต่พวกเขาก็ยังสามารถแก้ไขปัญหาที่สลับซับซ้อนมากๆได้ไม่ยาก

ในขั้นตอนเขียน spec อยู่นี้ ถ้าเทียบกับ Deming Cycle (PDCA) จะอยู่ในช่วงของ Plan-Do-Check และเมื่อมันผ่านแล้ว ก็จะนำไป Do นั่นก็คือการเขียน code นั่นเองครับ

แนวคิดบล็อคนี้จะอยู่ในพื้นที่ สีแดงอ่อนๆตามรูปข้างล่างนี้ครับ

Spec Concept

ส่วนสีส้มอ่อนๆเป็นส่วนที่ผมตั้งใจบอกคุณว่า สิ่งที่ผมคิดเขียน spec นี้จะเชื่อมโยงกับ code ของโปรแกรมจริงๆได้ด้วยวิธีการ หรือ เทคนิคอะไร

หัวข้อนี้จะนำเสนอโครงสร้างเขียน Spec ในรูปแบบของ Gherkin ผมได้แสดงเป็นตัวอย่างความต้องการของผลิตภัณฑ์ง่ายๆ ดังต่อไปนี้

ตัวอย่าง:

ความต้องการของลูกค้าเจ้าของผลิตภัณฑ์ตู้เติมบุญ เรื่องมีอยู่ว่า ลูกค้า หยอดเงินลงไปเท่าไรก็ได้  ให้ตู้เติมบุญ พร้อมกับใส่เบอร์โทรศัพท์ของลูกค้าลงไปด้วยแล้วกดเติมเงิน บริการตู้เติมบุญต้องนำเงินเติมเข้าเบอร์โทรศัพท์ที่ลูกค้าระบุมา พร้อมกับบอกว่าทำธุรกรรมนี้เสร็จสมบูรณ์แล้ว

เอาละ หลังจากอ่านความต้องการ เสร็จแล้วก่อนอื่น เราจะต้องวิเคราะห์ก่อน แล้วคุยไป คุยมากับลูกค้า จนเราและลูกค้าเข้าใจตรงกันว่า ความต้องการที่ลูกค้าต้องการนั้น คืออะไร (What) และ มันทำงาน หรือ ประติสัมพันธ์กับผู้ใช้อย่างไร (How) เพื่อที่เราจะนำไปเขียน spec ได้ในลำดับต่อไป ซึ่ง spec จะต้องประกอบด้วย

มันคืออะไร (What / Plan หรือ สร้าง Hypothesis) 

ชื่อความต้องการนั้น เราจะเรียกมันว่า “Feature” แล้วตามด้วยรายละเอียดที่เราเก็บมาจากลูกค้าด้วยก็ดี

ในแต่ละความต้องการ จะประกอบไปด้วยสถานะ (State) ต่างๆ เมื่อเลียงกันเป็นเรื่องเป็นราว เราจะเรียกว่า สถานการณ์ (Scenario) ซึ่งเราจะต้องคุยกับลูกค้ามาให้ได้สัก 1 สถานการณ์ ถ้าไม่มี จากความต้องการที่เราคุยกับลูกค้ามา เราก็ต้องมาสร้างสถานะการณ์ให้ได้ เพื่อที่เราจะมั่นใจได้ว่า เมื่อเรานำ spec นี้ไปเขียน code สร้าง program แล้ว สิ่งที่เราทำขึ้นมันถูกต้องตามที่ลูกค้าต้องการ

เราจะเรียก state มาเลียงเป็นลำดับกันนี้ว่า “Scenario”

สิ่งที่กำหนดให้ผลิตภันฒ์รับรู้หรือนำเข้าได้ หรือ Input / Domain เรียกว่า “Given”

ชื่อกิจกรรม หรือพฤติกรรมที่ตอบสนองความต้องการนี้ได้ หรือ Function เรียกว่า “When”

และ ผลลัพย์ที่เป็นไปได้ ในขณะที่เราวิเคราะห์อยู่นี้ หรือ Output / Range เรียกว่า “Then”

จากการคุยกันกับลูกค้า ผมเขียน spec ในส่วนของ what ได้แล้ว แบบนี้

Feature:  เติมเงินเข้าโทรศัพท์กับตู้เติมบุญ

Scenario: มีเงินอยู่ในโทรศัพท์ 10 บาท, ลูกค้าหมายเลข 12345678, หยอดเงินเติมเข้าไป 100

Given: ให้ mobile หมายเลข 12345678 มีเงิน 10 บาท, เติมบุญรับเงินเข้ามา 100 บาท

When: กดเติมเงิน

Then: mobile หมายเลข 12345678 ต้องมีเงิน 110 บาท และ เติมบุญรับเงินเข้ามาเหลือ 0 บาท

Scenario: ลูกค้าใส่หมายเลขโทรศัพท์ผิด

Given: ให้ mobile หมายเลข 87654321 ไม่มีในระบบ, เติมบุญรับเงินเข้ามา 1000 บาท

When: กดเติมเงิน

Then: ตู้เติมบุญต้องพูดว่า “กรุณาใส่เบอร์โทรศัพท์ใหม่ค่า”

——————————————————–

ถ้าเขียน Domain , Range และ States ที่เป็นไปได้ ทั้งหมดจาก spec ตอนนี้ ก็จะได้แบบนี้

Domain = {

(12345678, มีเงิน 10 บาท, รับเงินเข้ามา 100 บาท ),

(87654321, 0, รับเงินเข้ามา 1000 บาท )

}

Range = {

(มีเงิน 110, รับเงินเข้ามาเหลือ 0, “เสร็จสมบูรณ์”),

(0, 0, “กรุณาใส่เบอร์โทรศัพท์ใหม่ค่า”)

}

States ={

( (12345678, มีเงิน 10 บาท, รับเงินเข้ามา 100 บาท), (มีเงิน 110, รับเงินเข้ามาเหลือ 0, “เสร็จสมบูรณ์”) ),

( (87654321, 0, รับเงินเข้ามา 1000 บาท), (0, 0, “กรุณาใส่เบอร์โทรศัพท์ใหม่ค่า”) )

}

มันทำงานอย่างไร (How / Do & Check หรือ พิสูจน์ Hypothesis) 

ขั้นต่อไป ผมจะต้องเขียน How ครับ เพื่อให้ spec ของผมสมบูรณ์ก่อนนำไป เขียน code ซึ่งมันก็คือ Algorithms ในโครงสร้างเขียน spec นี้ เรียกว่า “Step”

การเขียน step เป็นส่วนที่บอกว่า ความต้องการของลูกค้าทำงานอย่างไร คิดง่ายๆ step มันก็คือ state หลายๆ state รวมกันก็คือ สถานะการณ์ ที่เกิดขึ้น หรือเป็นไปได้ทั้งหมดของความต้องการ หรือ ประสบการณ์ของผู้ใช้ครับ

ในที่นี้มี 2 สถานะการณ์ เมื่อค่าที่เราระบุไว้ที่ given, ผ่านหลายๆ step นั่นคือ when แล้ว ผลลัพย์ สุดท้ายของเราจะต้องอยู่ใน rage ที่เรากำหนดไว้ใน range แล้วอย่างถูกต้องทุกๆ สถานะการณ์ ที่เราคิดไว้ตลอดไป นั่นคือความมั่นใจว่า เราเข้าใจปัญหา แล้วแก้ไขมันได้อย่างถูกต้องก่อนนำไปเขียน code หรือ สร้างโปรแกรมนั่นเอง

ผมเขียน step ออกมา ได้แบบนี้

step 1: เริ่มต้นให้ mobile_number, money_amt = 0, termgern_machine,

r = (money_amt, termgern_machine, “”) โดยที่

mobile_number คือหมายเลขโทรศัพท์ลูกค้าที่รับเข้ามา,

money_amt คือ จำนวนเงินของหมายเลขโทรศัพท์นี้,

termboon_machine คือ จำนวนเงินที่ลูกค้าหยอดเข้าเครื่องเติมบุญเข้ามา,

r คือ เวกเตอร์ n-Tuple ของสถานะสุดท้าย เมื่อจบ step

step 2: ให้ money_amt = เงินของหมายเลข mobile_number

step 3: เติมเงิน money_amt = money_amt + termgern_machine

step 4: r = (money_amt, termgern_machine, “เสร็จสมบูรณ์”)

step 5: จบขั้นตอน

ผมเขียนเริ่มต้น 5 ขั้นตอน เราลองเอาไป พิจารณาทีละ สถานะการณ์กันครับ เริ่มจาก

Scenario: มีเงินอยู่ในโทรศัพท์ 10 บาท, ลูกค้าหมายเลข 12345678, หยอดเงินเติมเข้าไป 100

Given: ให้ mobile หมายเลข 12345678 มีเงิน 10 บาท, เติมบุญรับเงินเข้ามา 100 บาท

step 1: mobile_number = 12345678, money_amt = 0, termgern_machine = 100

step 2: money_amt = 10 (กำหนดมาให้เท่ากับ 10 บาท)

step 3: money_amt = 110

step 4: r = (110, 100, “เสร็จสมบูรณ์”)

step 5: จบขั้นตอน

พิจารณาจาก Then คือ state ของ range ที่เป็นไปได้นั่นก็คือ

Then: mobile หมายเลข 12345678 ต้องมีเงิน 110 บาท และ เติมบุญรับเงินเข้ามาเหลือ 0 บาท

หรือ r = (110, 0, “เสร็จสมบูรณ์”) เมื่อเทียบกับ step ที่ 4 r คือสุดท้ายมีค่าเท่ากับ  (110, 100, “เสร็จสมบูรณ์”) ที่ออกมา จึงไม่ถูกต้องใช่มั้ยครับ ผมจึงกลับไป แก้ไข step ของผม ได้มาแบบนี้

step 2: ให้ money_amt = เงินของหมายเลข mobile_number

step 3: เติมเงิน money_amt = money_amt + termgern_machine

step 4: termgern_machine = 0

step 5: r = (money_amt, termgern_machine, “เสร็จสมบูรณ์”)

step 6: จบขั้นตอน

แก้ไขเสร็จแล้ว ก็กลับมาพิจารณาใหม่ครับ… ฝากไว้ให้คิดต่อ ลองไล่ step ดูซิว่าถูกต้องแล้วหรือยัง

ผมคิดว่า ผู้อ่าน คงจะเข้าใจ ได้ไม่ยาก เรื่องการเขียน step นี้มีเครื่องมือหลายตัว ช่วยสร้าง step จากโครงสร้าง spec แบบ Gherkin ก็ขึ้นอยู่กับ Framework ที่เราต้องการ ลองค้นหาดูนะครับ ต่อจากนี้ผมคิดว่า ทำไม่ยากแล้วถ้าเข้าใจหลักการเขียน spec คือ What และ How แบบนี้

บล็อคนี้ผมตั้งใจนำเสนอ หลักการเขียน spec ในรูปแบบของ Gherkin ซึ่งเป็น แนวคิดสำคัญของ BDD ก็คือต้องการจะบอกเพื่อนๆโปรแกรมเมอร์ให้เข้าใจปัญหาก่อนเสมอๆ ว่าปัญหา หรือ ความต้องการของลูกค้าเราจริงๆแล้วมันคืออะไร และ เราจะแก้ไขมันได้ด้วยขั้นตอน หรือ algoruthm อย่างไรบ้าง แล้วลองไล่ขั้นตอนได้ทันที เพื่อพิสูจน์ให้ถูกต้องเป็นเบื้องขั้นตอนแรกก่อน ที่จะลงมือเขียน code เพื่อสร้างโปรแกรมออกมานั่นเอง

เพื่อให้เข้าใจหลักการสำคัญของการเขียน spec มากขึ้น ผมจะ เขียน spec ในรูปแบบ Gherkin กับปัญหาเกมส์ Fizz buzz ในบล็อกที่แล้วของผมอีกตัวอย่าง (ก็กลับไปอ่าน spec จากบล็อกที่แล้วของผมก่อนนะครับ )

คุณจะเห็นว่า spec มันเขียนด้วยโครงสร้างอะไรก็ไม่รู้ แต่มีอธิบายปัญหา อะไร และ ทำอย่างไร ไว้ครบแล้ว ผมก็จะเขียนใหม่ให้อยู่ในรูปแบบ Gherkin ครับ ได้แบบนี้

Feature:  เกม Fizz buzz มหาสนุก

Scenario: ผู้เล่นนับถึงเลข n, ผู้เล่นจะต้องพูด “fizz” ถ้า n หาร 3 ลงตัว, พูด “buzz” ถ้า n หาร 5 ลงตัว, พูด “fizz buzz” ถ้า n หาร 3 และ 5 ลงตัว หรือ พูด เลขนั้นออกไป ถ้าไม่เข้าเงื่อนไข

Given: N คือ เลขที่ผู้เล่นนับถึง

N = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ]

When: ผู้เล่นพูด

Then: W คือ คำที่ผู้เล่นต้องพูดสำหรับเกม Fizz buzz จะต้องเท่ากับ

W = [ “หนึ่ง”, “สอง”, “fizz”, … , “สิบสี่”, “fizz buzz” ]

ต่อไปเขียน step ครับ แบบนี้

step 1: เริ่มต้น i = 1, n = N[i], W’ = หาคำพูดของตัวเลขจาก N ทั้งหมด, s = (N, W’)

step 2: ถ้า n หาร 3 หรือ 5 ลงตัว ให้ W'[i] = “fizz buzz” ถ้าไม่ ให้ตรวจสอบต่อ

step 3: ถ้า n หาร 3 ลงตัว ให้ W'[i] = “fizz” ถ้าไม่ ให้ตรวจสอบต่อ

step 4: ถ้า n หาร 5 ลงตัว ให้ W'[i] = “buzz” จบเงื่อนไข

step 5: i = i + 1, n = N[i]

step 6: ถ้า n มีค่า ไป step 2 ->

step 7: จบขั้นตอน

คุณจะเห็นว่า ผมเขียน step แบบเป็นแค่เอกสารเพื่ออธิบาย algorithm เท่านั้น จริงๆแล้วส่วนสำคัญที่สุด คือ spec ส่วนของ what ที่ผมได้ใช้โครงสร้าง Gherkin เขียนให้ดูอยู่ในรูปแบบของ Feature-Given-When-Then แค่นั้นเอง

การเขียน step นั้นสามารถเขียนในรูปแบบอย่างใดๆก็ได้ ซึ่งไม่ใช่เขียนเป็นขั้นตอนอย่างนี้ เช่น UML Sequence Diagram, Work Flow, Activities Diagram, Storyboard, Story Mapping, วิดิโอ การ์ตูน หรือ อะไรก็แล้วแต่ ที่เล่าเรื่อง เล่าสถานการณ์เป็นฉากๆ เป็นขั้นเป็นตอนได้อีกมากมาย ถ้าคุณเข้าใจการเขียน spec ว่ามันประกอบด้วย มีสิ่งใดบ้างที่เกี่ยวกับปัญหา หรือ ความต้องการ (What) และ วิธีการแก้ปัญหา หรือ เพื่อตอบสนองความต้องการมันจะต้องทำงานร่วมกันอย่างไร (How) แค่นี้นะครับ ก็ลองนำไป พิจารณาดูได้

ผมขอจบบล็อกไว้เพียงเท่านี้ หวังว่าคงจะเป็นประโยชน์บ้าง

 ขอบคุณครับ

#:P

Advertisements

#agile, #bdd, #specification, #tdd, #test-first