องค์ประกอบที่สำคัญที่สุดอย่างหนึ่งของการออกแบบซอฟต์แวร์ที่ดีคือ mindset ที่คุณนำมาใช้เมื่อต้องทำงานเขียนโปรแกรม หลายองค์กรมักส่งเสริม tactical mindset ที่เน้นการทำให้ฟีเจอร์ต่างๆ ทำงานได้เร็วที่สุดเท่าที่จะทำได้ อย่างไรก็ตาม หากคุณต้องการ design ที่ดี คุณต้องใช้แนวทางเชิงกลยุทธ์ (strategic approach) มากขึ้น โดยลงทุนเวลาเพื่อสร้าง clean designs และแก้ไขปัญหา บทนี้จะอธิบายว่าทำไม strategic approach ถึงสร้าง design ที่ดีกว่า และจริงๆ แล้วยังมีต้นทุนที่ถูกกว่า tactical approach ในระยะยาว

3.1    Tactical Programming (การเขียนโปรแกรมเชิงยุทธวิธี)

โปรแกรมเมอร์ส่วนใหญ่เข้าสู่การพัฒนาซอฟต์แวร์ด้วย mindset ที่ผมเรียกว่า tactical programming ในแนวทาง tactical สิ่งที่คุณให้ความสำคัญหลักคือการทำให้อะไรสักอย่างทำงานได้ เช่น ฟีเจอร์ใหม่หรือการแก้บั๊ก เมื่อมองผิวเผินก็ดูสมเหตุสมผลดี: จะมีอะไรสำคัญไปกว่าการเขียนโค้ดที่ทำงานได้ล่ะ? แต่ tactical programming ทำให้แทบเป็นไปไม่ได้ที่จะสร้าง system design ที่ดี

ปัญหาของ tactical programming คือมันเป็นแนวทางที่สายตาสั้น ถ้าคุณเขียนโปรแกรมแบบ tactical คุณจะพยายามทำงานให้เสร็จโดยเร็วที่สุด บางทีคุณอาจมี deadline ที่กระชั้นชิด ผลคือการวางแผนสำหรับอนาคตไม่ใช่สิ่งที่ให้ความสำคัญ คุณไม่ต้องใช้เวลามากในการมองหา design ที่ดีที่สุด คุณแค่อยากให้อะไรสักอย่างทำงานได้เร็วๆ คุณบอกตัวเองว่าไม่เป็นไรถ้าต้องเพิ่มความซับซ้อนเล็กน้อยหรือใส่ทางลัด (kludge) เข้าไปบ้าง ถ้ามันช่วยให้งานปัจจุบันเสร็จเร็วขึ้น

นี่คือวิธีที่ระบบต่างๆ กลายเป็นซับซ้อน ดังที่ได้กล่าวไว้ในบทที่แล้ว ความซับซ้อน (complexity) นั้นเพิ่มขึ้นทีละเล็กทีละน้อย ไม่ใช่เรื่องใดเรื่องหนึ่งโดยเฉพาะที่ทำให้ระบบซับซ้อน แต่เป็นการสะสมของสิ่งเล็กๆ น้อยๆ นับสิบหรือร้อยอย่าง ถ้าคุณเขียนโปรแกรมแบบ tactical งานเขียนโปรแกรมแต่ละงานจะเพิ่มความซับซ้อนเหล่านี้เข้าไปอีกนิดหน่อย แต่ละอย่างอาจดูเหมือนเป็นการประนีประนอมที่สมเหตุสมผลเพื่อให้งานปัจจุบันเสร็จเร็วขึ้น แต่ความซับซ้อนเหล่านี้สะสมตัวอย่างรวดเร็ว โดยเฉพาะถ้าทุกคนเขียนโปรแกรมแบบ tactical

ไม่นานนัก ความซับซ้อนบางอย่างจะเริ่มก่อให้เกิดปัญหา และคุณจะเริ่มเสียใจที่ใช้ทางลัดเหล่านั้นตั้งแต่แรก แต่คุณจะบอกตัวเองว่าการทำให้ฟีเจอร์ถัดไปทำงานได้สำคัญกว่าการย้อนกลับไป refactor โค้ดเดิม การ refactor อาจช่วยได้ในระยะยาว แต่แน่นอนว่ามันจะทำให้งานปัจจุบันช้าลง ดังนั้นคุณจึงมองหาทางแก้ไขอย่างเร่งด่วน (quick patches) เพื่อแก้ปัญหาที่คุณเจอ นี่แค่สร้างความซับซ้อนมากขึ้น ซึ่งก็ต้องใช้ patches เพิ่มขึ้นอีก ไม่ช้าโค้ดก็จะกลายเป็นเรื่องยุ่งเหยิง แต่เมื่อถึงจุดนี้สถานการณ์ก็แย่มากจนต้องใช้เวลาเป็นเดือนในการทำความสะอาด ตารางงานของคุณไม่มีทางรองรับความล่าช้าแบบนั้นได้ และการแก้ไขปัญหาแค่หนึ่งหรือสองอย่างก็ดูเหมือนจะไม่ช่วยอะไรเท่าไหร่ คุณก็เลยแค่เขียนโปรแกรมแบบ tactical ต่อไป

ถ้าคุณเคยทำงานในโปรเจกต์ซอฟต์แวร์ขนาดใหญ่มาระยะหนึ่ง ผมว่า คุณคงเคยเห็น tactical programming ในการทำงานและเคยประสบปัญหาที่ตามมาแล้ว เมื่อคุณเริ่มเดินบนเส้นทาง tactical แล้ว มันยากที่จะเปลี่ยนแปลง

เกือบทุกองค์กรพัฒนาซอฟต์แวร์จะมีนักพัฒนาอย่างน้อยหนึ่งคนที่เล่น tactical programming แบบสุดขั้ว: นั่นคือ tactical tornado tactical tornado คือโปรแกรมเมอร์ที่ผลิตรหัสออกมาได้เร็วกว่าคนอื่นมาก แต่ทำงานในลักษณะ tactical อย่างสิ้นเชิง เมื่อพูดถึงการ implement ฟีเจอร์แบบเร่งด่วน ไม่มีใครทำได้เร็วกว่า tactical tornado ในบางองค์กร ผู้บริหารมองว่า tactical tornado คือฮีโร่ แต่ tactical tornado ทิ้งร่องรอยความเสียหายไว้เบื้องหลัง พวกเขาแทบจะไม่เคยถูกมองว่าเป็นฮีโร่โดยวิศวกรคนอื่นๆ ที่ต้องทำงานกับโค้ดของพวกเขาในอนาคต โดยปกติแล้ว วิศวกรคนอื่นๆ จะต้องทำความสะอาดความยุ่งเหยิงที่ tactical tornado ทิ้งไว้ ซึ่งทำให้ดูเหมือนว่าวิศวกรเหล่านั้น (ซึ่งเป็นฮีโร่ตัวจริง) กำลังทำงานช้ากว่า tactical tornado

3.2    Strategic Programming (การเขียนโปรแกรมเชิงกลยุทธ์)

ก้าวแรกสู่การเป็นนักออกแบบซอฟต์แวร์ที่ดีคือการตระหนักว่า โค้ดที่ทำงานได้ยังไม่เพียงพอ การเพิ่มความซับซ้อนที่ไม่จำเป็นเพื่อให้งานปัจจุบันเสร็จเร็วขึ้นนั้นไม่ใช่สิ่งที่ยอมรับได้ สิ่งที่สำคัญที่สุดคือโครงสร้างระยะยาวของระบบ โค้ดส่วนใหญ่ในระบบใดๆ ก็ตามถูกเขียนขึ้นโดยการต่อยอดจาก code base ที่มีอยู่ ดังนั้นหน้าที่ที่สำคัญที่สุดของคุณในฐานะนักพัฒนาคือการอำนวยความสะดวกให้กับการต่อยอดในอนาคตเหล่านั้น ดังนั้นคุณไม่ควรมองว่า "โค้ดที่ทำงานได้" เป็นเป้าหมายหลัก แม้ว่าแน่นอนว่าโค้ดของคุณต้องทำงานได้ก็ตาม เป้าหมายหลักของคุณคือการสร้าง design ที่ยอดเยี่ยม ซึ่งบังเอิญว่ามันก็ทำงานได้ด้วยเช่นกัน นี่คือ strategic programming

Strategic programming ต้องใช้ investment mindset (mindset การลงทุน) แทนที่จะใช้เส้นทางที่เร็วที่สุดในการทำโปรเจกต์ปัจจุบันให้เสร็จ คุณต้องลงทุนเวลาเพื่อปรับปรุง design ของระบบ การลงทุนเหล่านี้อาจทำให้คุณช้าลงเล็กน้อยในระยะสั้น แต่มันจะทำให้คุณเร็วขึ้นในระยะยาว ดังที่แสดงใน Figure 3.1

การลงทุนบางอย่างจะเป็นการ proactive (เชิงรุก) เช่น การใช้เวลาเพิ่มอีกนิดเพื่อหา design ที่เรียบง่ายสำหรับแต่ละคลาสใหม่ แทนที่จะ implement ไอเดียแรกที่นึกออก ให้ลอง design ทางเลือกสองสามแบบแล้วเลือกแบบที่สะอาดที่สุด ลองจินตนาการถึงวิธีต่างๆ ที่ระบบอาจต้องถูกเปลี่ยนแปลงในอนาคต และทำให้แน่ใจว่ามันจะทำได้ง่ายด้วย design ของคุณ การเขียน documentation ที่ดีก็เป็นอีกตัวอย่างหนึ่งของการลงทุนเชิงรุก

การลงทุนอื่นๆ จะเป็นแบบ reactive (เชิงรับ) ไม่ว่าคุณจะลงทุนมากแค่ไหนล่วงหน้า ก็จะยังมีข้อผิดพลาดในการตัดสินใจออกแบบของคุณอย่างหลีกเลี่ยงไม่ได้ เมื่อเวลาผ่านไป ข้อผิดพลาดเหล่านี้จะชัดเจนขึ้น เมื่อคุณพบปัญหาด้าน design อย่าแค่ ignore หรือแก้ไขแบบฉาบฉวย (patch around it) ให้ใช้เวลาเพิ่มอีกนิดเพื่อแก้ไขมัน ถ้าคุณเขียนโปรแกรมแบบ strategic คุณจะปรับปรุง design ของระบบอย่างต่อเนื่องทีละเล็กทีละน้อย นี่คือสิ่งที่ตรงกันข้ามกับ tactical programming ที่คุณเพิ่มความซับซ้อนเล็กๆ น้อยๆ อย่างต่อเนื่องซึ่งจะก่อให้เกิดปัญหาในอนาคต

3.3    How Much to Invest? (ควรลงทุนเท่าไหร่?)

แล้วจำนวนการลงทุนที่เหมาะสมคือเท่าไหร่? การลงทุนล่วงหน้าจำนวนมาก เช่น การพยายามออกแบบทั้งระบบตั้งแต่ต้น จะไม่ได้ผล นี่คือวิธี waterfall ซึ่งเรารู้ว่ามันใช้ไม่ได้ design ที่ดีมักจะค่อยๆ เกิดขึ้นเป็นชิ้นเป็นส่วน เมื่อคุณเริ่มมีประสบการณ์กับระบบ ดังนั้นแนวทางที่ดีที่สุดคือการลงทุนเล็กๆ น้อยๆ อย่างต่อเนื่อง ผมแนะนำให้ใช้เวลาประมาณ 10–20% ของเวลาพัฒนาทั้งหมดไปกับการลงทุน จำนวนนี้เล็กพอที่จะไม่ส่งผลกระทบต่อตารางงานของคุณอย่างมีนัยสำคัญ แต่ก็มากพอที่จะสร้างประโยชน์อย่างมีนัยสำคัญเมื่อเวลาผ่านไป โปรเจกต์แรกๆ ของคุณจะใช้เวลานานกว่า 10–20% เมื่อเทียบกับแนวทาง tactical ล้วนๆ เวลาที่เพิ่มขึ้นนั้นจะส่งผลให้ได้ software design ที่ดีขึ้น และคุณจะเริ่มเห็นประโยชน์ภายในไม่กี่เดือน อีกไม่นานคุณจะพัฒนาได้เร็วขึ้นอย่างน้อย 10–20% เมื่อเทียบกับถ้าคุณเขียนโปรแกรมแบบ tactical เมื่อถึงจุดนี้การลงทุนของคุณจะกลายเป็น free: ประโยชน์จากการลงทุนในอดีต จะประหยัดเวลาได้มากพอที่จะครอบคลุมต้นทุนของการลงทุนในอนาคต คุณจะได้คืนต้นทุนของการลงทุนเริ่มต้นอย่างรวดเร็ว Figure 3.1 แสดงให้เห็นปรากฏการณ์นี้

img

Figure 3.1: ในตอนเริ่มต้น แนวทาง tactical ในการเขียนโปรแกรมจะทำให้ความคืบหน้าเร็วขึ้นกว่าแนวทาง strategic อย่างไรก็ตาม ความซับซ้อนจะสะสมตัวเร็วกว่าภายใต้แนวทาง tactical ซึ่งลด productivity ลง เมื่อเวลาผ่านไป แนวทาง strategic จะให้ผลลัพธ์ที่มากกว่า หมายเหตุ: รูปนี้มีไว้เพื่อเป็นภาพประกอบเชิงคุณภาพเท่านั้น ผู้เขียนไม่ทราบถึงการวัดเชิงประจักษ์ใดๆ เกี่ยวกับรูปร่างที่แน่นอนของกราฟเหล่านี้

ในทางกลับกัน ถ้าคุณเขียนโปรแกรมแบบ tactical คุณจะทำโปรเจกต์แรกๆ เสร็จเร็วขึ้น 10–20% แต่เมื่อเวลาผ่านไป ความเร็วในการพัฒนาจะช้าลงเมื่อความซับซ้อนสะสมตัว อีกไม่นานคุณจะเขียนโปรแกรมช้าลงอย่างน้อย 10–20% คุณจะสูญเสียเวลาที่ประหยัดได้ตอนต้นไปอย่างรวดเร็ว และตลอดช่วงชีวิตที่เหลือของระบบ คุณจะพัฒนาช้ากว่าถ้าคุณใช้แนวทาง strategic ถ้าคุณไม่เคยทำงานใน code base ที่เสื่อมสภาพอย่างหนัก ลองพูดคุยกับคนที่เคยเจอ พวกเขาจะบอกคุณว่าคุณภาพโค้ดที่ไม่ดีทำให้การพัฒนาช้าลงอย่างน้อย 20%

3.4    Startups and Investment (Startup และการลงทุน)

ในบางสภาพแวดล้อมมีแรงผลักดันที่แข็งแกร่งที่ต่อต้านแนวทาง strategic ตัวอย่างเช่น startups ในช่วงแรกๆ มีแรงกดดันมหาศาลให้ปล่อยเวอร์ชั่นแรกๆ ออกมาให้เร็ว ในบริษัทเหล่านี้อาจดูเหมือนว่าแม้แต่การลงทุน 10–20% ก็ยังไม่สามารถจ่ายได้ ผลคือ startups จำนวนมากใช้แนวทาง tactical ใช้ความพยายามเพียงเล็กน้อยกับ design และยิ่งน้อยไปกว่านั้นกับการทำความสะอาดเมื่อเกิดปัญหา พวกเขาให้เหตุผลกับตัวเองว่าถ้าธุรกิจประสบความสำเร็จ พวกเขาจะมีเงินพอที่จะจ้างวิศวกรเพิ่มเพื่อมาทำความสะอาด

ถ้าคุณอยู่ในบริษัทที่โน้มเอียงไปในทิศทางนี้ คุณควรตระหนักว่าเมื่อ code base กลายเป็น spaghetti แล้ว มันแทบเป็นไปไม่ได้ที่จะแก้ไข คุณอาจจะต้องจ่ายต้นทุนการพัฒนาที่สูงตลอดอายุของผลิตภัณฑ์ นอกจากนี้ ผลตอบแทนของ design ที่ดี (หรือไม่ดี) ก็เกิดขึ้นเร็วพอสมควร ดังนั้นจึงมีโอกาสสูงที่แนวทาง tactical จะไม่ช่วยให้ผลิตภัณฑ์แรกของคุณออกมาเร็วขึ้นด้วยซ้ำ

อีกสิ่งที่ควรพิจารณาคือหนึ่งในปัจจัยที่สำคัญที่สุดต่อความสำเร็จของบริษัทคือคุณภาพของวิศวกร วิธีที่ดีที่สุดในการลดต้นทุนการพัฒนาคือการจ้างวิศวกรที่ยอดเยี่ยม: พวกเขาไม่ได้มีต้นทุนสูงกว่าวิศวกรธรรมดามากนัก แต่มี productivity ที่สูงกว่าอย่างมหาศาล อย่างไรก็ตาม วิศวกรที่ดีที่สุดใส่ใจอย่างมากกับ design ที่ดี ถ้า code base ของคุณเละเทะ ข่าวจะแพร่กระจายออกไป และนั่นจะทำให้คุณ recruit คนเก่งได้ยากขึ้น ผลคือคุณมีแนวโน้มที่จะได้วิศวกรระดับธรรมดา ซึ่งจะเพิ่มต้นทุนในอนาคตและอาจทำให้โครงสร้างระบบเสื่อมโทรมลงไปอีก

Facebook เป็นตัวอย่างของ startup ที่ส่งเสริม tactical programming เป็นเวลาหลายปีที่คำขวัญของบริษัทคือ "Move fast and break things" วิศวกรใหม่ที่เพิ่งจบมหาวิทยาลัยได้รับการสนับสนุนให้กระโดดเข้าไปใน code base ของบริษัททันที เป็นเรื่องปกติที่วิศวกรจะ push commits ขึ้น production ในสัปดาห์แรกของการทำงาน ในด้านบวก Facebook สร้างชื่อเสียงในฐานะบริษัทที่ empower พนักงานของตน วิศวกรมีอิสระอย่างมาก และมีกฎหรือข้อจำกัดน้อยมากที่จะมาขัดขวาง

Facebook ประสบความสำเร็จอย่างมากในฐานะบริษัท แต่ code base ของมันได้รับผลกระทบจากแนวทาง tactical ของบริษัท โค้ดส่วนใหญ่ไม่เสถียรและเข้าใจยาก มี comment และ tests น้อยมาก และยากที่จะทำงานด้วย เมื่อเวลาผ่านไปบริษัทก็ตระหนักว่าวัฒนธรรมของมันไม่ยั่งยืน ในที่สุด Facebook ก็เปลี่ยนคำขวัญเป็น "Move fast with solid infrastructure" เพื่อส่งเสริมให้วิศวกรลงทุนกับ good design มากขึ้น ยังต้องรอดูกันต่อไปว่า Facebook จะสามารถทำความสะอาดปัญหาที่สะสมมานานหลายปีของ tactical programming ได้สำเร็จหรือไม่

เพื่อความเป็นธรรมกับ Facebook ผมควรชี้ให้เห็นว่าโค้ดของ Facebook อาจไม่ได้แย่ไปกว่าค่าเฉลี่ยของ startups ทั่วไป Tactical programming เป็นเรื่องปกติในหมู่ startups Facebook เป็นเพียงตัวอย่างที่เห็นได้ชัดเป็นพิเศษ

โชคดีที่มันก็เป็นไปได้ที่จะประสบความสำเร็จใน Silicon Valley ด้วยแนวทาง strategic Google และ VMware เติบโตขึ้นในช่วงเวลาเดียวกันกับ Facebook แต่ทั้งสองบริษัทนี้ใช้แนวทางที่เน้น strategic มากกว่า ทั้งสองบริษัทให้ความสำคัญอย่างมากกับโค้ดคุณภาพสูงและ design ที่ดี และทั้งสองบริษัทสร้างผลิตภัณฑ์ที่ซับซ้อนซึ่งแก้ปัญหาที่ยากด้วยระบบซอฟต์แวร์ที่เชื่อถือได้ วัฒนธรรมทางเทคนิคที่แข็งแกร่งของทั้งสองบริษัทเป็นที่รู้จักกันดีใน Silicon Valley มีบริษัทอื่น เพียงไม่กี่แห่งที่จะแข่งขันกับพวกเขาในการจ้างบุคลากรด้านเทคนิคระดับสูง

ตัวอย่างเหล่านี้แสดงให้เห็นว่าบริษัทสามารถประสบความสำเร็จได้ด้วยทั้งสองแนวทาง อย่างไรก็ตาม การทำงานในบริษัทที่ใส่ใจในการออกแบบซอฟต์แวร์และมี code base ที่สะอาดนั้นสนุกกว่ามาก

3.5    Conclusion (บทสรุป)

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

สิ่งสำคัญคือต้องสอดคล้องกันในการใช้แนวทาง strategic และมองการลงทุนเป็นสิ่งที่ต้องทำวันนี้ ไม่ใช่พรุ่งนี้ เมื่อคุณอยู่ในช่วงเวลาคับขัน มันจะดึงดูดใจให้เลื่อนการทำความสะอาดออกไปจนกว่าช่วงคับขันจะผ่านพ้น แต่นี่คือทางลาดชัน (slippery slope) หลังจากช่วงคับขันปัจจุบัน ก็แทบจะแน่นอนว่าจะมีช่วงคับขันต่อไปอีก และอีกเรื่อยๆ เมื่อคุณเริ่มเลื่อนการปรับปรุง design ออกไป มันง่ายมากที่การเลื่อนนั้นจะกลายเป็นถาวร และวัฒนธรรมของคุณจะเลื่อนไปสู่แนวทาง tactical ยิ่งคุณรอนานที่จะแก้ปัญหาด้าน design ปัญหาก็จะยิ่งใหญ่ขึ้น วิธีการแก้ปัญหาก็จะยิ่งน่ากลัวขึ้น ซึ่งทำให้คุณเลื่อนมันออกไปได้ง่ายยิ่งขึ้นไปอีก วิธีการที่มีประสิทธิภาพที่สุดคือวิธีที่วิศวกรทุกคนลงทุนเล็กๆ น้อยๆ อย่างต่อเนื่องในการออกแบบที่ดี