ข้าม​ไป​ยัง​เนื้อหา

Tesler’s Law

ความ​ซับซ้อน​ย้าย​ที่​ได้ แต่​กำจัด​ไม่​ได้ — คำถาม​คือ​ใคร​จะ​เป็น​ผู้​แบกรับ

Tesler’s Law หรือ​ชื่อ​ทางการ​ว่า Law of Conservation of Complexity เป็น​ข้อสังเกต​จาก​วงการ human-computer interaction (HCI) ที่​ระบุ​ข้อ​จำกัด​พื้นฐาน​ของ​การ​ออกแบบ​ซอฟต์แวร์: ทุก application มี​ความ​ซับซ้อน​แก่น​แท้​จำนวน​หนึ่ง​ที่ ลด​ทอน​ไม่​ได้ (irreducible) คุณ​ย้าย​มัน​จาก​ที่​หนึ่ง​ไป​อีก​ที่​หนึ่ง​ได้ แต่​ทำให้​มัน​หาย​ไป​เฉย ๆ ไม่​ได้ — เหมือน​กฎ​อนุรักษ์​พลังงาน​ใน​ฟิสิกส์​ที่​พลังงาน​เปลี่ยน​รูป​ได้​แต่​ไม่​สูญหาย

“ทุก application มี​ความ​ซับซ้อน​ที่​ลด​ทอน​ไม่​ได้​ใน​ตัว คำถาม​เดียว​คือ​ใคร​จะ​ต้อง​รับมือ​กับ​มัน — ผู้​ใช้ นัก​พัฒนา app หรือ​นัก​พัฒนา platform”

— Larry Tesler

Larry Tesler เป็น​นัก​วิทยาการ​คอมพิวเตอร์​ที่ Xerox PARC ใน​ช่วง​กลาง​ทศวรรษ 1980s ก่อน​จะ​ย้าย​ไป​มี​บทบาท​สำคัญ​ใน​การ​พัฒนา Lisa และ Macintosh ที่ Apple เขา​เป็น​ผู้​คิดค้น​แนวคิด cut/copy/paste ที่​เรา​ใช้​กัน​ทุก​วัน​นี้ Tesler เริ่ม​พูด​ถึง​กฎ​นี้​ตอน​ช่วย​พัฒนา​ภาษา​ออกแบบ​เชิง​โต้ตอบ (interactive design language) ที่ Xerox PARC โดย​ตั้งใจ​ใช้​เป็น​ข้อ​โต้แย้ง​เชิงกลยุทธ์​เพื่อ​โน้มน้าว​ผู้​บริหาร​บริษัท​และ​ผู้​พัฒนา​ซอฟต์แวร์​อิสระ ให้​ยอม​ลงทุน​แรงงาน​วิศวกร​เพิ่ม​ขึ้น​เพื่อ​ลด​ภาระ​ของ​ผู้​ใช้​ปลายทาง เรื่องราว​นี้​ถูก​บันทึก​ไว้​ใน​บท​สัมภาษณ์ Tesler ใน​หนังสือ Designing for Interaction ของ Dan Saffer ซึ่ง​เป็น​ที่มา​หลัก​ที่​ทำให้​กฎ​นี้​แพร่หลาย​ใน​วงการ UX

ที่มา​ของ​ชื่อ “Law of Conservation” ตั้งใจ​ล้อ​กับ​กฎ​อนุรักษ์​พลังงาน​ใน​วิชา​ฟิสิกส์​โดยตรง — พลังงาน​ไม่​ถูก​สร้าง​ขึ้น​ใหม่​หรือ​ทำลาย​ทิ้ง​ได้ เปลี่ยน​รูป​ได้​เท่านั้น ใน​ทำนอง​เดียวกัน ความ​ซับซ้อน​ของ​ปัญหา​หนึ่ง ๆ ก็มี “ปริมาณ​คงตัว” อยู่​ระดับ​หนึ่ง​ที่​การ​ออกแบบ​ใด ๆ ก็​ตัด​ทิ้ง​ไป​เฉย ๆ ไม่​ได้ ทำได้​แค่​ย้าย​มัน​ไป​ยัง​จุด​อื่น​ใน​ระบบ

หัวใจ​ของ​กฎ​นี้​ไม่ใช่​การ​บอก​ให้ “ลด​ความ​ซับซ้อน” แบบ​เลื่อนลอย แต่​คือ​การ​ยอมรับ​ความ​จริง​ที่​ว่า ความ​ซับซ้อน​แก่น​แท้ (irreducible complexity) ของ​ปัญหา​หนึ่ง ๆ มี​ปริมาณ​คงที่​อยู่​ปริมาณ​หนึ่ง เช่น การ​จัด​ตาราง​นัดหมาย​ข้าม​เขต​เวลา​ที่​ต่าง​กัน​ย่อม​มี​ตรรกะ​ซับซ้อน​บาง​อย่าง​ที่​หนี​ไม่​พ้น สิ่ง​ที่​ทีม​ออกแบบ​ทำได้​จริง ๆ คือ ตัดสิน​ใจ​อย่าง​จงใจ​ว่า​ใคร​จะ​แบก​มัน​ไว้ ระหว่าง​ผู้​เล่น​สาม​ฝ่าย:

  • ผู้​ใช้ (user) — ต้อง​เรียนรู้ ตั้ง​ค่า หรือ​ทำ​ขั้นตอน​เพิ่ม​เอง​ด้วย​มือ
  • นัก​พัฒนา application (application developer) — เขียน code เพิ่ม​เพื่อ​ซ่อน​ความ​ซับซ้อน​ไว้​เบื้องหลัง UI ที่​เรียบ​ง่าย
  • นัก​พัฒนา platform (platform developer) — ผลัก​ภาระ​ไป​ไว้​ที่ framework, library หรือ​ระบบ​ปฏิบัติการ​ชั้น​ล่าง​สุด

Tesler เชื่อ​มั่น​อย่าง​แรง​กล้า​ว่า​ภาระ​ควร​ตก​อยู่​ที่​วิศวกร​มากกว่า​ผู้​ใช้​ปลายทาง เขา​ให้​เหตุผล​เชิง​เศรษฐศาสตร์​ไว้​อย่าง​คมคาย​ว่า

“ถ้า​ผู้​ใช้​นับ​ล้าน​คน​ต้อง​เสีย​เวลา​คนละ​หนึ่ง​นาที​ต่อ​วัน​ไป​กับ​ความ​ซับซ้อน​ที่​วิศวกร​คน​หนึ่ง​สามารถ​กำจัด​ได้​ด้วย​การ​ทำงาน​เพิ่ม​แค่​หนึ่ง​สัปดาห์ โดย​ทำให้​ซอฟต์แวร์​ซับซ้อน​ขึ้น​อีก​นิด นั่น​แปล​ว่า​คุณ​กำลัง​ลงโทษ​ผู้​ใช้​เพื่อ​ให้​งาน​ของ​วิศวกร​ง่าย​ขึ้น”

นี่​คือ​การ​เทียบ “ต้นทุน​รวม” (aggregate cost) ไม่ใช่​ต้นทุน​ต่อ​หน่วย — เวลา​ของ​ผู้​ใช้​หนึ่ง​ล้าน​คน​คูณ​กัน​แล้ว​มี​ค่า​มากกว่า​เวลา​ของ​วิศวกร​หนึ่ง​คน​เสมอ แนวคิด​นี้​เป็น​รากฐาน​ของ​หลักการ Progressive Disclosure และ​ปรัชญา​การ​ออกแบบ smart default ที่​ซอฟต์แวร์​ยุค​หลัง​ใช้​กัน​แพร่หลาย

อย่างไร​ก็ตาม Bruce Tognazzini นัก​ออกแบบ UX ผู้​มีชื่อเสียง​อีก​คน​หนึ่ง​เสนอ​มุมมอง​แย้ง​ที่​น่า​สนใจ: มนุษย์​มัก​ไม่​ยอม​ให้​ความ​ซับซ้อน​ใน​ชีวิต​ลด​ลง​จริง ๆ เมื่อ​เครื่องมือ​ถูก​ทำให้​ง่าย​ขึ้น ผู้​ใช้​กลับ​พยายาม​ทำงาน​ที่​ซับซ้อน​ขึ้น​ไป​อีก​ขั้น​ด้วย​เครื่องมือ​เดิม (เช่น Excel ที่​เรียบ​ง่าย​ขึ้น​เรื่อย ๆ กลับ​ถูก​ใช้​สร้าง model การเงิน​ที่​ซับซ้อน​กว่า​เดิม) ผล​คือ “ความ​ซับซ้อน​ที่​ถูก​อนุรักษ์​ไว้” อาจ​ไม่​ได้​อยู่​นิ่งเฉย แต่​ขยาย​ตัว​ไป​พร้อม​กับ​ความ​ทะเยอทะยาน​ของ​งาน​ที่​ผู้​ใช้​พยายาม​ทำ

ควร​แยก​กฎ​นี้​ออก​จาก​แนวคิด essential vs. accidental complexity ของ Fred Brooks ใน “No Silver Bullet” ให้​ชัดเจน: Brooks พูด​ถึง​ความ​ซับซ้อน​ของ “ปัญหา domain” เทียบ​กับ​ความ​ซับซ้อน​ที่​เกิด​จาก “เครื่องมือ/กระบวนการ” ที่​เรา​เลือก​ใช้ ส่วน Tesler’s Law พูด​ถึง “ตำแหน่ง” ที่​ความ​ซับซ้อน​แก่น​แท้​จะ​ไป​ตก​อยู่ — คนละ​มิติ​กัน แต่​เสริม​กัน​ได้​ดี: essential complexity ตาม Brooks คือ​ปริมาณ​ที่ Tesler บอกว่า “ลด​ทอน​ไม่​ได้” นั่นเอง

  • เครื่องมือ​จัด​ตาราง​นัดหมาย (เช่น Calendly) — การ​หา​ช่วง​เวลา​ว่าง​ที่​ตรง​กัน​ข้าม​เขต​เวลา เทียบ availability ของ​หลาย​ฝ่าย และ​จัดการ​การ​ยกเลิก/เลื่อน​นัด เป็น​ความ​ซับซ้อน​ที่​หนี​ไม่​พ้น เครื่องมือ​เหล่า​นี้​เลือก​ให้​ทีม​วิศวกร​แบก​ตรรกะ​ซับซ้อน​ไว้​เบื้องหลัง เพื่อ​ให้​ผู้​ใช้​เห็น​แค่​ปุ่ม “เลือก​เวลา” ง่าย ๆ ตรง​ข้าม​กับ​การ​ส่ง​อีเมล​ไป​มา​ต่อรอง​เวลา​นัดหมาย​ที่​ผลัก​ภาระ​ทั้งหมด​กลับ​ไป​ที่​มนุษย์
  • Autocomplete และ autocorrect — ความ​ซับซ้อน​ของ​ภาษา​ธรรมชาติ การ​สะกด​คำ และ​บริบท ถูก​ย้าย​จาก​ผู้​ใช้ (ต้อง​พิมพ์​ถูก​ทุก​ตัว​อักษร​เอง) ไป​ไว้​ที่​นัก​พัฒนา platform keyboard/เบราว์เซอร์
  • การ​ตั้ง​ค่า network ของ​อุปกรณ์​บ้าน (router, smart TV) — ผู้​ผลิต​บาง​ราย​เลือก​ซ่อน​ความ​ซับซ้อน​ของ DHCP, DNS, port forwarding ไว้​หลัง auto-configuration ขณะ​ที่​บาง​ราย​เปิด​เมนู​ตั้ง​ค่า​เต็ม​รูปแบบ​ให้​ผู้​ใช้​ขั้น​สูง​จัดการ​เอง — เป็น​ตัวอย่าง​ชัด​ของ “เลือก​ว่า​ใคร​แบก”
  • Checkout ของ e-commerce — การ​คำนวณ​ภาษี ค่า​ส่ง ส่วนลด และ​วิธี​ชำระ​เงิน​ที่​รองรับ​หลาย​ประเทศ​เป็น​ความ​ซับซ้อน​แก่น​แท้​ที่​หนี​ไม่​พ้น ผู้​ให้​บริการ​อย่าง payment gateway สมัย​ใหม่​เลือก​แบก​ความ​ซับซ้อน​นี้​ไว้​ใน​บริการ​ของ​ตนเอง (เช่น คำนวณ​ภาษี​ตาม​ที่​อยู่​อัตโนมัติ) เพื่อ​ให้​ทั้ง​ผู้​ใช้​ปลายทาง​และ​นัก​พัฒนา​ร้าน​ค้า​เห็น​แค่​หน้า​จอ checkout ที่​เรียบ​ง่าย ตรง​ข้าม​กับ​ระบบ​เก่า​ที่​ให้​ผู้​ดูแล​ร้าน​ค้า​ต้อง​ตั้ง​ค่า​กฎ​ภาษี​เอง​ที​ละ​ประเทศ
  • Library และ framework ที่​ซ่อน retry/backoff ไว้​ให้ — ตัวอย่าง​เชิง code: ถ้า​ปล่อย​ให้​ผู้​เรียก​ใช้ (caller) ต้อง​เขียน retry loop เอง​ทุก​จุด​ที่​เรียก API ภายนอก ความ​ซับซ้อน​ของ transient failure จะ​ตก​อยู่​ที่​ผู้​ใช้ library (นัก​พัฒนา app) แต่​ถ้า library platform ซ่อน​มัน​ไว้ ความ​ซับซ้อน​ก็​ย้าย​ไป​อยู่​ที่​นัก​พัฒนา library แทน:
// แบบที่ 1: ผู้เรียกใช้ (นักพัฒนา app) ต้องแบกความซับซ้อนของ retry เอง
var attempt = 0;
var response = await httpClient.GetAsync(url);
while (!response.IsSuccessStatusCode && attempt < maxRetries)
{
await Task.Delay(ComputeBackoff(attempt));
response = await httpClient.GetAsync(url);
attempt++;
}
// แบบที่ 2: ย้ายความซับซ้อนไปไว้ที่นักพัฒนา platform (คนเขียน ResilientHttpClient)
// ผู้เรียกใช้เห็นแค่ interface ง่าย ๆ ตรรกะ retry/backoff ถูกซ่อนไว้ข้างใน
var response = await resilientClient.GetAsync(url);

code ทั้ง​สอง​แบบ​แก้​ปัญหา​เดียวกัน ความ​ซับซ้อน​ของ transient failure ไม่​ได้​หาย​ไป​ไหน มัน​แค่​ถูก​ย้าย​ที่ — คำถาม​คือ​ใคร​ควร​เขียน​และ​ดูแล ComputeBackoff และ retry loop นี้​ครั้ง​เดียว​ใน​ที่​เดียว (library) หรือ​ให้​ทุก​จุด​เรียก​ต้อง​เขียน​ซ้ำ​เอง

flowchart LR
    Problem[ปัญหาที่มีความซับซ้อนแก่นแท้] --> Choice{ใครจะแบกรับ}
    Choice --> User[ผู้ใช้ทำเองด้วยมือ]
    Choice --> AppDev[นักพัฒนา app เขียน code ซ่อนไว้]
    Choice --> PlatformDev[นักพัฒนา platform ซ่อนไว้ใน library]
    AppDev --> SimpleUI[UI ที่ผู้ใช้เห็นเรียบง่ายขึ้น]
    PlatformDev --> SimpleAPI[API ที่นักพัฒนา app เรียกใช้ง่ายขึ้น]
  • ตั้ง​คำถาม​อย่าง​จงใจ ไม่ใช่​ปล่อย​ให้​เกิด​โดย​บังเอิญ — ทุก​ครั้ง​ที่​พบ​ความ​ซับซ้อน​ที่​หนี​ไม่​พ้น ให้​ถาม​ตรง ๆ ว่า “ใคร​ควร​แบก​สิ่ง​นี้” แทนที่​จะ​ปล่อย​ให้​มัน​ตก​ไป​อยู่​กับ​ผู้​ใช้​โดย​ไม่มี​ใคร​ตัดสิน​ใจ
  • เปรียบเทียบ​ต้นทุน​รวม ไม่ใช่​ต้นทุน​ต่อ​ครั้ง — เวลา​ของ​วิศวกร​หนึ่ง​สัปดาห์​เทียบ​กับ​เวลา​ผู้​ใช้​นับ​ล้าน​คน​สะสม​กัน​ไป​เรื่อย ๆ มัก​จะ​คุ้ม​เสมอ​ที่​จะ​ลงทุน​ฝั่ง​วิศวกรรม
  • อย่า​ลืม​ว่า​ความ​ซับซ้อน​ที่​ซ่อน​ไว้​ยัง​ต้อง​มี​คน​ดูแล — เมื่อ​ย้าย​ภาระ​ไป​ที่​นัก​พัฒนา platform หรือ app code ที่​ซ่อน​ความ​ซับซ้อน​นั้น​เอง​ก็​ต้อง​ทดสอบ ดูแล และ​แก้ bug ใน​ระยะ​ยาว การ​ซ่อน​ไม่ใช่​การ​กำจัด
  • ใช้ Progressive Disclosure เป็น​เครื่องมือ — เปิดเผย​ความ​ซับซ้อน​แบบ​เป็น​ชั้น ๆ ให้​ผู้​ใช้​ขั้น​สูง​เข้าถึง​ได้​เมื่อ​จำเป็น โดย​ไม่​บังคับ​ผู้​ใช้​ทั่วไป​ให้​เห็น​ทุก​อย่าง​ตั้งแต่​แรก
  • ระวัง​การ​ตีความ​ผิด​ว่า “ซับซ้อน​ขึ้น​เสมอ​ดี​กว่า” — กฎ​นี้​ไม่​ได้​บอก​ให้​เพิ่ม​ความ​ซับซ้อน​ของ code พร่ำเพรื่อ หลัก YAGNI และ Keep It Simple ยัง​ใช้​คู่​กัน​ได้: ลงทุน​แก้​ความ​ซับซ้อน แก่น​แท้ ที่​มี​อยู่​จริง อย่า​สร้าง​ความ​ซับซ้อน เทียม ขึ้น​มา​ใหม่​โดย​อ้าง​กฎ​นี้
  • จำ​มุมมอง​ของ Tognazzini ไว้​ด้วย — การ​ลด​ความ​ซับซ้อน​ของ​เครื่อง​มือหนึ่ง​อาจ​ถูก​ผู้​ใช้​ผลักดัน​ให้​ทำงาน​ที่​ทะเยอทะยาน​ขึ้น​จน​ความ​ซับซ้อน​กลับ​มา​ใหม่​ใน​รูปแบบ​อื่น การ​ออกแบบ​จึง​ควร​มอง​เป็น​วงจร​ต่อ​เนื่อง ไม่ใช่​แก้​ครั้ง​เดียว​จบ