แรงจูงใจสำหรับ Clean Code

Mar 13 2023
ไม่ว่าโครงการซอฟต์แวร์จะมีขนาดเท่าใดก็ตาม เราจำเป็นต้องเป็นส่วนหนึ่งของทีมใดก็ตาม ไม่ว่าจะเล็กหรือใหญ่ ดังนั้นจึงเป็นเรื่องสำคัญมากที่: โค้ดที่เราเขียนควรสามารถอ่านและพัฒนาได้โดยนักพัฒนารายอื่น

ไม่ว่าโครงการซอฟต์แวร์จะมีขนาดเท่าใดก็ตาม เราจำเป็นต้องเป็นส่วนหนึ่งของทีมใดก็ตาม ไม่ว่าจะเล็กหรือใหญ่ ดังนั้นจึงเป็นเรื่องสำคัญมากที่: โค้ดที่เราเขียนควรสามารถอ่านและพัฒนาได้โดยนักพัฒนารายอื่น

ศิลปะของการเขียนโปรแกรมโดย Geek & Poke ได้รับอนุญาตภายใต้ CC BY 3.0

“บิ๊ก” เดฟ โธมัส (ผู้ก่อตั้ง OTI เจ้าพ่อแห่งกลยุทธ์ Eclipse) ให้นิยามโค้ดสะอาดด้วยประโยคเหล่านี้:

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

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

คนโง่ทุกคนสามารถเขียนโค้ดที่คอมพิวเตอร์เข้าใจได้ โปรแกรมเมอร์ที่ดีจะเขียนโค้ดที่มนุษย์สามารถเข้าใจได้ มาร์ติน ฟาวเลอร์

เหตุใดจึงต้องล้างรหัส

ไม่ยากที่จะคาดเดาว่าจะเกิดอะไรขึ้นเมื่อพัฒนาด้วยโค้ดที่ไม่สะอาด ฉันต้องการพูดคุยเกี่ยวกับปัญหาบางอย่างที่เราจะต้องเผชิญ

หากรหัสไม่สะอาด

  • สร้างหนี้ทางเทคนิค (หากคุณต้องการดู: “https://en.wikipedia.org/wiki/Technical_debt”) เราสามารถพูดคุยเกี่ยวกับการจัดการหนี้ทางเทคนิคภายใต้หัวข้ออื่น แต่อาจมีสาเหตุหลายประการสำหรับหนี้ทางเทคนิค ตัวจุดไฟแรกของไส้ตะเกียงนี้ยังเป็นรหัสที่ไม่สะอาดอีกด้วย
  • เราพบการบล็อกโค้ดที่ไม่สามารถบำรุงรักษาได้ เมื่อเวลาผ่านไป โครงการอาจไม่สามารถบำรุงรักษาได้เช่นกัน
  • จะมีค่าใช้จ่ายในการพัฒนา ค่าใช้จ่ายในการพัฒนาจะสูงกว่าที่ควรจะเป็น หากทีมพัฒนาไม่สามารถคาดการณ์ค่าใช้จ่ายได้ นั่นเป็นข้อบ่งชี้ที่ชัดเจนว่าเรามีโครงการที่ไม่ได้พัฒนาอย่างหมดจด
  • ความสับสนเติบโต ด้วยคุณสมบัติที่เพิ่มเข้ามา แต่ละบล็อคโค้ดที่พัฒนาขึ้น ความสับสนจะเพิ่มขึ้น ซึ่งหมายความว่าโครงการตายเมื่อเวลาผ่านไป คุณอาจเคยได้ยินมามาก: “มาเขียนสิ่งนี้ใหม่กันเถอะ”

ก่อนอื่น เราต้องการการรับรู้ของสมาชิกทุกคนในทีมและสถาบันที่เป็นเจ้าของโครงการ เนื่องจากการบรรลุรหัสที่สะอาดจะเสียเวลา (เงินมีค่าตามธรรมชาติ เวลา = เงิน)

เราจำเป็นต้องมีแรงจูงใจในการจับคู่การเขียนโปรแกรมและการปรับโครงสร้างใหม่กับสมาชิกคนอื่นๆ ในทีม

เราจำเป็นต้องสื่อสารเพิ่มเติมกับทีมด้านเทคนิคและการค้าเพื่อให้แน่ใจว่าเราสามารถอธิบายและหารือเกี่ยวกับโมดูลหรือบล็อคโค้ดได้

Clean coding ไม่ใช่ทักษะที่สามารถหาได้ในชั่วข้ามคืน เป็นนิสัยที่ต้องพัฒนาโดยคำนึงถึงหลักการเหล่านี้และนำไปใช้ทุกครั้งที่คุณเขียนโค้ด Yiğit Kemal Erinç

กรอบรหัสสะอาด

เรียบง่าย เน้นความสมบูรณ์ถูกต้อง

เพื่อให้เรายอมรับว่าปฏิบัติตามหลักการของ Clean Code;

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

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

public List<string> GetTags(int[] a)
{
  var list = new List<string>();
  
  for (var b = 0; b < theList.Count; b++)
  {
    if (theList[b].CityCodes.Intersect(a).Any())
    {
      list.Add(theList[b].TagName);
     }
   }

   return list;
}

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

public List<string> GetTagNamesThatMatchedCityCodes(int[] cityCodes)
{
  var tagNamesThatMatchedCityCodes = new List<string>();
  foreach (var tag in locationTags)
  {
    if (tag.CityCodes.Intersect(cityCodes).Any())
    {
      tagNamesThatMatchedCityCodes.Add(tag.TagName);
     }
   }

    return tagNamesThatMatchedCityCodes;
 }

หากเราต้องการเพิ่มประสิทธิภาพโดยเฉพาะอย่างยิ่งสำหรับชุดข้อมูลขนาดใหญ่ เราก็สามารถเลือกใช้ "yield return" ได้เช่นกัน แต่วิธีนี้อาจลดความสามารถในการอ่านโค้ด ในความไม่แน่ใจดังกล่าว เราอาจต้องตัดสินใจว่าความสามารถในการอ่านหรือประสิทธิภาพของโปรแกรมมีความสำคัญต่อเราหรือไม่ ตัวอย่างเช่น ถ้าฉันกำลังพัฒนาอัลกอริทึมการบีบอัด ฉันต้องการให้มันทำงานอย่างมีประสิทธิภาพ แต่ถ้าฉันกำลังพัฒนาโปรแกรมสำหรับธุรกิจและโปรแกรมจะไม่สร้างภาระให้กับระบบมากเกินไป ฉันยอมรับได้ที่จะสูญเสียประสิทธิภาพบางส่วนเพื่อให้มีโค้ดที่อ่านได้และบำรุงรักษาได้

public IEnumerable<string> GetTagNamesThatMatchedCityCodes(int[] cityCodes)
{
  foreach (var tag in tags)
  {
    if (tag.CityCodes.Intersect(cityCodes).Any())
    {
      yield return tag.TagName;
     }
   }
}

นักพัฒนา นักวิเคราะห์ธุรกิจ เจ้าของผลิตภัณฑ์ ฯลฯ ชื่อควรชัดเจนและเข้าใจได้สำหรับสมาชิกทุกคนในทีม Eric Evans ให้นิยามแนวคิดของ " ภาษาที่แพร่หลาย " ในหนังสือของเขา "Domain-Driven Design: Tackling Complexity in the Heart of Software" ซึ่งเป็นหนังสืออีกเล่มที่ฉันอยากแนะนำให้อ่านหากคุณมีโอกาส

ภาษาที่แพร่หลายเป็นหนึ่งในองค์ประกอบสำคัญของการจัดการกับความซับซ้อน การรับรู้นี้จะช่วยป้องกันอุบัติเหตุในการทำงาน

ฟังก์ชั่นสั้นและเน้น

เราควรพัฒนาฟังก์ชั่นให้สั้นและเน้นที่สุด พวกเขาควรมีภารกิจเดียวเท่านั้น (ความรับผิดชอบเดียว) และพวกเขาควรทำภารกิจนี้ให้สุดความสามารถ การรับรู้นี้จะป้องกันการทำซ้ำรหัส

วิธีการที่มีโค้ดหลายบรรทัดจะเพิ่มความซับซ้อน โดยเฉพาะอย่างยิ่งหากเรามีคำสั่ง if หรือบล็อกโค้ดที่มีการวนซ้ำ เราสามารถย่อบล็อกโค้ดให้สั้นลงได้โดยใช้วิธีการใหม่ๆ เพื่อให้โค้ดเข้าใจได้มากขึ้น นี่เป็นหัวข้อสำคัญในการตรวจสอบรหัสเพื่อหลีกเลี่ยงความซับซ้อนที่เพิ่มขึ้น

private void CreatePluralDependentMessageParts(int orderCount) 
{
  if (orderCount == 0) {
    FormatMessageForCustomerWithNoOrder();
   } else if (orderCount == 1) {
     FormatMessageForCustomerWithSingleOrder();
   } else {
     FormatMessageForCustomerWithManyOrders(orderCount);
   }
 }

ออบเจ็กต์การถ่ายโอนข้อมูลเป็นวิธีที่สะอาดในการถ่ายโอนข้อมูลระหว่างเลเยอร์หรือฟังก์ชันของแอปพลิเคชัน

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

public Customer(string name, string surname, string email, 
string appKey, int age, bool isPremium)
{
      Name = name;
      Surname = surname;
      Email = email;
      AppKey = appKey;
      Age = age;
      IsPremium = isPremium;
}

public Customer(CustomerDto customerDto)
 {
      Name = customerDto.Name;
      Surname = customerDto.Surname;
      Email = customerDto.Email;
      AppKey = customerDto.AppKey;
      Age = customerDto.Age;
      IsPremium = customerDto.IsPremium;
 }

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

หากโค้ดไม่ชัดเจน เราควรใช้การ Refactoring โค้ดมากกว่าการแสดงความคิดเห็น อย่างไรก็ตามสามารถแสดงความคิดเห็นในสถานการณ์ที่ต้องอธิบายได้

// Check to see if the order is eligible for shipping
if (order.Payment.IsCompleted && order.Delivery.Slot.IsAvailable)

if (order.IsEligibleForShipping())

หลีกเลี่ยงการใช้ตัวเลขมหัศจรรย์ในรหัสของคุณ การใช้ค่าคงที่ที่ค้นหาได้จะช่วยให้คุณเข้าใกล้โค้ดที่สะอาดขึ้น นอกจากนี้ เมื่อคุณต้องการเปลี่ยนแปลงตรรกะทางธุรกิจของคุณ คุณจะสามารถแก้ไขได้อย่างง่ายดายโดยไม่ต้องสแกนโค้ดทั้งหมด

if (password.Length > 10)
{
   throw new ValidationException($"Must be at least 10 characters");
}
if (password.Length > MAX_PASSWORD_LENGTH)
{
    throw new ValidationException($"Must be at least {MAX_PASSWORD_LENGTH} characters");
}

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

public bool IsValidPassword(string password)
 {
   if (string.IsNullOrEmpty(password))
     throw new ValidationException("Cannot be null or empty");
   if (password.Any(char.IsLetterOrDigit))
     throw new ValidationException("Must contain at least one special character");
   if (password.Length > MAX_PASSWORD_LENGTH)
     throw new ValidationException($"Must be at least {MAX_PASSWORD_LENGTH} characters");

    return true;
  }

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

ขอบคุณที่สละเวลา.

Okan Yüksel วิศวกรซอฟต์แวร์

อ้างอิง

  • Robert C. Martin, 'Clean Code: คู่มือของงานฝีมือซอฟต์แวร์ Agile'
  • อคิน คัลดิโรกลู Clean Code
  • Yiğit Kemal Erinç,https://www.freecodecamp.org/news/clean-coding-for-beginners/
  • ซันนี่สุลต่านhttps://sunnysultan1640.medium.com/what-is-bad-code-how-to-write-clean-code-a9b7b539ad8