Blog

  • สร้าง Job ทำงานตามเวลา ด้วย Kubernetes CronJob

    เกรินนำ

    ผมหายไปนาน เลยกลับมาเขียนซ้อมบทความที่เบาหน่อยแล้วกัน

    พอดีได้รับงานที่ต้อง migrate application พวก window schedules task ในระบบเดิมให้ขึ้นไป run บน containers Kubernetes เลยเขียนบทความนี้ไว้ แชร์กัน

    การทำงานของ applications

    job เป็น command line application รับ arguments พิมพ์ข้อมูลแล้ว แสดงข้อมูลออกมาเฉยๆที่หน้าจอแบบนี้

    > dotnet TimerCmd.dll MooDeng

    ตัว CronJob จะ run job นี้ ทุกๆ นาที (cron)

    สิ่งที่ต้องติดตั้งก่อน

    1. Visual Studio 2022 Community Edition

    2. Docker Desktop เลือก Setting แล้วเปิด Enable Kubernetes

    สร้าง application

    1. เปิด visual studio 2022

    2. Create a new project เลือก Console App ภาษา C# ตั้งชื่อว่า TimerCmd

    3. กดเข้าไปที่ Program.cs file ใส่ code เข้าไปตามนี้

    Console.WriteLine($"{args[0]} จะ deng deng {DateTime.Now.ToString("วันที่ dd/MM/yyyy เวลา HH:mm")}.");

    Build application Job

    1. เปิด cmd แล้ว cd เข้าไปที่ folder ของ app TimerCmd

    2. พิมคำสั่ง docker init เพื่อสร้าง build image Dockerfile

    ให้เลือก 
    > ASP.NET Core - (detected) suitable for an ASP.NET Core application 
    แล้วกด enter ลงมาจนจบ

    3. พิมคำสั่ง docker build . -t timer-cmd:1

    เป็นการสร้าง image docker ชื่อ timer-cmd กำหนด tag เท่ากับ 1 ไปเก็บไว้ที่ local Docker Desktop image ก่อน

    เข้าไปเปิด Docker Desktop จะพบ image แบบนี้

    4. สร้าง Kube manifest file สำหรับ deploy CronJob ของเราไปที่ Kubernetes containers

    สร้าง file ชื่อ cronjob-every-minute.yaml

    apiVersion: batch/v1
    kind: CronJob1
    metadata:
      name: timer-cmd2
    spec:
      schedule: "* * * * *"3
      successfulJobsHistoryLimit: 14
      concurrencyPolicy: Forbid5
      jobTemplate:
        spec:
          template:
            spec:
              containers:
              - name: timer-cmd-container6
                image: timer-cmd:17
                args:
                - MooDeng
              restartPolicy: Never8
    1. ระบุชนิด resource ที่ที่จะสร้าง object ที่ run ใน Kubernetes คือ CronJob โดย Cron คือ ตามเวลาที่กำหนดหรือ schedule เหมือนกับการตั้ง schedule ใน window task schedule และ job ก็คือ task ที่เราเขียน application ที่เราต้องการให้ทำงาน task ตาม Cron หรือตามเวลาที่กำหนด ↩︎
    2. ชื่อ CronJob คือ timer-cmd ปกติจะตั้งเป็นชื่อเดียวกับ application ↩︎
    3. ตั้งเวลารูปแบบ cron ใช้ app ช่วยสร้างได้ที่ https://crontab.cronhub.io ↩︎
    4. กำหนดให้เก็บ history ของ pod ไว้เพื่อดู log ของ app ย้อนหลัง 1 pod ค่าปกติเป็น 3 ↩︎
    5. Forbid กำหนดให้ run job ได้ทีละ 1 job เท่านั้น job ถัดมาจะถูกข้ามไปถ้า job ปัจจุบันยัง run ไม่จบ กำหนดได้ อีก 2 แบบ Allow คือ ยอมให้ run job พร้อมกันได้ตามเวลาที่ตั้งไว้แม้ job ก่อนหน้ายัง run ไม่จบ และ Replace คือ terminate job ปัจจุบุนทิ้งทันทีแม้ยัง run ไม่เสร็จ และเริ่ม job ใหม่ทันทีตามเวลาที่ตั้งไว้ ↩︎
    6. ชื่อ container ที่ทำงานอยู่ใน pod ตั้งชื่อ application แล้วตามด้วย -container ↩︎
    7. ชื่อ image ของ application timer-cmd ที่เรา build ไว้แล้ว และ 1 คือ tag version ของ application ↩︎
    8. Never จะไม่มีการ restart container อัตโนมัติ ถ้า container หรือ application ใน container เกิดข้อผิดพลาดอะไรก็แล้วแต่ สามารถกำหนดค่าได้อีกได้แก่ Always คือ restart container ให้อัตโนมัติหากเกิดข้อผิดพลาดใดๆ และ OnFailure คือ restart container กรณี container ออกแบบ error หรือ return ค่าสถานะที่ไม่ใช่ 0 ↩︎

    5. พิมคำสั่งเพื่อ deploy Job เราที่ run ทุกๆนาที

    > kubectl apply -f cronjob-every-minute.yaml

    คำสั่งดู status job ที่ run อยู่ครับ

    > kubectl get jobs –watch

    เมื่อปล่อยไว้สัก 1 นาที ให้ดู pod log ว่า job เราทำงานมั้ย

    > kubectl get pods

    จะแสดง pods ทั้งหมดที่ run อยุ่ออกมา

    ให้พิมคำสั่งนี้เพื่อดู log ใน pod

    > kubectl pods timer-cmd-28868523-7969n

    ถ้าต้องการลบ cron job ใส่คำสั่งนี้

    > kubectl delete cronjob timer-cmd

    จบแล้วครับการสร้าง Job บน Kubernetes ที่เป็น container technology แทนการ run job บน task schedule แบบดั้งเดิม

    ขอบคุณครับ #:P

  • Singularity กับ นิพพาน

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

    #:P

  • องค์ประกอบของกฏ(Rules)

    กฏ(Rules) หรือ เฟรมเวิร์ค(Framework) คือชุดของประโยค หรือ code ที่ประกอบด้วย
    ผู้คน(Actors)หรือ สิ่งที่กระทำกิจกรรมได้
    กิจกรรม(Activities) คือ กระบวนการ หรือพฤติกรรมต่างๆร่วมกัน ของ ผู้คน
    และ
    สิ่งที่ได้ออกมาจากกิจกรรม(Artifacts)

    กฏ​ จะประกอบด้วย 3​ สิ่งนี้ย่อๆว่า​ AAA… ในงานพัฒนา​สินค้า​และบริการ​ หรือใช้แก้ปัญหา​เฉพาะใดๆเราจะเรียกว่า​ เฟรมเวิร์ค​ เช่น​ Scrum,​ .Net​/Spring framework
    กฏ​ ในทางสังคมเราจะเรียก​ กฏหมาย, รัฐธรรมนูญ, พระราชบัญญัติ​ แล้วแต่
    ศาสนา​ก็มี​ เรียกกฏว่า​ ศีล, ธรรมวินัย

    จะเห็นว่า​ กฎนั้น​ ประกอบด้วย​
    Actors ผู้เล่น​ ผู้กระทำ​ ผู้ถูกกระทำ บทบาท​ เป็นได้ทุกสิ่ง​
    Activities กิจกรรม​ หรือ​ การกระทำ​ใดๆ​ หรือ​ ฟังก์ชั่น​ โดย Actors
    และ​สุดท้าย​
    Artifacts ผลลัพธ์​หรือ​สิ่งที่เป็นผล​ออกมาจาก​กิจกรรมใดๆจาก​ Activities

    เมื่อผู้คนหรือสิ่งใดๆ อยู่รวมกัน จึงมีกฏอยู่ร่วมกัน (Parties) ในทุกระดับ เช่น ครอบครัว, ทีม, หมู่บ้าน, ตำบล, อำเภอ, โรงเรียน, องค์กร, บริษัท, จังหวัด, พรรค, ประเทศ, โลก ยันจักรวาลในอนาคต

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

    #:P

  • สมการดุลยภาพ ของ Domain-Driven Design (DDD)

    หลักในการปฏบัติแบบ Domain-Driven Design มีอยู่ 3 ข้อ ได้แก่

    1. เรียนรู้ความหมายของปัญหาจากผู้เชี่ยวชาญ โดยใช้ภาษาร่วมกัน (Ubiquitous Language) หรือเรียกได้ว่า เป็นพื้นที่ความรู้ของปัญหา (The Problem Space)
    2. นำความรู้จากผู้เชี่ยวชาญที่ได้จากข้อ 1 มาออกแบบ (Modeling) เพื่อเขียน Code (Model-Driven Design) หรือเรียกได้ว่า พื้นที่ของความรู้ในการแก้ปัญหา (The Solution Space)
    3. พยายามป้องกันไม่ให้ความรู้จากปัญหา หรือ เทคนิคการออกแบบอื่นๆ มาทำให้ code จากข้อ 2 เปลี่ยนความหมายจากขอบเขตของปัญหาจากข้อ 1 ไป (Preserving Model Integrity)

    ดังนั้น ผมสามารถเขียน หลักการ DDD ออกมาได้สมการเป็น

    Ubiquitous Language = Model-Driven Design

    ความหมายของสมการแสดงถึงคงไว้ซึ่งดุลยภาพของ
    ความรู้ของปัญหา (ด้านซ้าย Ubiquitous Language) กับ Code (ด้านขวา Model-Driven Design) ให้ เท่ากัน อยู่เสมอไป (เครื่องหมายเท่ากับ = ในสมการ ก็คืองานส่วน Preserving Model Integrity)

    เครื่องมือสำหรับทำความเข้าใจปัญหาเพื่อใช้สร้างภาษาสำหรับสื่อสารและออกแบบ Model ร่วมกัน (Ubiquitous Language) โดย Eric Evan แนะนำไว้เรียกว่า Model Exploration Whirlpool

    เครื่องมือสำหรับงาน Model-Driven Design จากหนังสือ Domain-Driven Design: Tackling Complexity in the Heart of Software โดย Eric Evan ที่นำเสนอไว้ ดังภาพนี้ครับ

    งานในส่วนออกแบบนี้ สามารถใช้ Patterns ที่เรารู้จักเข้ามาช่วยออกแบบได้ครับ ผมแนะนำหนังสือเบื้อต้น ดังนี้

    เครื่องมือสำหรับงาน Preserving Model Integrity จากหนังสือ เล่มเดียวกัน โดย Eric Evan ที่นำเสนอไว้ ดังภาพนี้ครับ

     

    และ สำหรับวงสีน้ำเงิน  Ubiquitous Language (สมการทางด้านขวา) ก็คือ เครื่องมือที่ใช้สร้างภาษาร่วมกันระหว่างโปรแกรมเมอร์ และผู้ใช้ระบบ ซึ่งก็คือผู้เชี่ยวชาญเจ้าของปัญหา โดย Eric Evan แนะนำในหนังสือ นำเข้ามาใช้เป็นเครื่องมือสำหรับงานส่วนนี้ ก็เป็นหลักการปฏิบัติ ในกระบวนการพัฒนาซอฟแวร์ Extreme Programming ที่เรียกว่า System Metaphor

    Model-Driven Design คือ การออกแบบเชิงแบบจำลอง ทำไมจึงเป็นแบบจำลอง (Model) เพราะว่าเราต้องการแสดงความรู้ของปัญหาออกมาเป็นภาษาที่สื่อสารได้ง่าย, มีโครงสร้าง และ
    สามารถบันทึกเก็บไว้ได้

    ตัวอย่าง แบบจำลอง ก็คือ diagram ที่ใช้แสดงความหมายของความรู้ เช่น UML, Flow Chart, CAD หรือ Chart ในรูปแบบอื่นๆ เพื่อใช้สื่อความหมายของความรู้ที่ต้องการ รวมถึง syntax หรือ code ต่างๆด้วย เช่น Mathematical syntax, Python, C#, Go ก็ถือเป็น model ชนิดหนึ่งเช่นกัน

    ในความสมดุลจากสมการ จึงมีความหมายได้ว่า ความรู้จากปัญหา เท่ากับ Code ที่ออกแบบมาอยู่เสมอ เราจึงอ่าน Code ได้เหมือนได้อ่านเรื่องราวของปัญหาโดยไม่เสียความหมายจากเจ้าของปัญหาไปเลย

    นั่นก็คือหลักการสำคัญของ Domain-Driven Design

    ขอบคุณครับ

    #:P

  • .NET พัฒนาซอฟแวร์แบบ Resilient ด้วย package Polly

    สวัสดีครับ

    บล็อคนี้เป็นเรื่องยากสักหน่อย และผมจะใช้ภาษาของตัวเองเพื่ออธิบายการทำให้ระบบงานซอฟแวร์ที่เราสร้างขึ้นมีคุณสมบัติ resilient ก็คือ การรับประกันระบบซอฟแวร์ว่าจะทำงานชดเชย หรือ ฟื้นฟูให้ระบบกลับมาทำงานให้บริการเป็นปกติเหมือนเดิม หากเกิดความผิดพลาด (failures) และส่งผลให้เกิดความสูญเสีย (loss) ขึ้นแก่ธุรกิจ หรือ สิ่งอื่นๆที่เกี่ยวข้องกับระบบงานซอฟแวร์ของเราตามมา

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

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

    Resilient ไม่ได้รับประกันว่าจะไม่เกิดข้อผิดพลาด แต่รับประกันว่า หากเกิดข้อผิดพลาดแล้วระบบต้องสามารถชดเชย หรือฟื้นฟูตนเอง ให้กลับมาทำงานได้ตามปกติ

    การทำงานได้ตามปกติ หมายความว่า ระบบต้องทำงานโดยอยู่ในสถานะ ไม่มีข้อผิดพลาด

    ข้อผิดพลาดนี้ ไม่ใช่แค่ error เท่านั้น แต่มีความหมายรวมถึง metric ที่วัดค่า health, availability, performance, KPI, SLA และอื่นๆที่ใช้วัดคุณภาพของระบบซอฟแวร์ที่เรากำหนด ด้วย

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

    กระบวนการหลักของ Resilient ประกอบด้วย

    หนึ่ง แผนความคุ้มครองความผิดพลาด (Policy) และ สอง กระบวนการชดเชยความผิดพลาด (Compensate)

    Policy ก็คือ แผนความคุ้มครองความผิดพลาด ที่มีการระบุเงื่อนไขไว้เพื่อจับข้อผิดพลาด ที่อาจจะเกิดขึ้นจากกระบวนการทำงานปกติ

    ตัวอย่างที่จะทำเป็นตัวอย่างนี้ใช้ package Polly ติดตั้งได้โดยเขาไปที่ visual studio 2017 แล้วพิมพ์คำสั่ง ข้างล่างนี้ลงไปที่ package console manager

    PM> install-package Polly

    Polly จัดเตรียมเครื่องมือสำหรับแผนความคุ้มครองให้เลือกใช้ ดังนี้

    Retry: แผนนี้จะ จับข้อผิดพลาดแล้วนับสะสมไว้ จากนั้นจะชดเชยข้อผิดพลาดในแต่ละครั้งไป ไม่เกินจำนวนครั้งที่กำหนด

    ตัวอย่าง code Retry

    public void SimpleRetry()
    {
    int countFoo = 0;
    Action foo = () =>
    {
    ++countFoo;
    Console.WriteLine($”Act foo: {countFoo}”);
    throw new NullReferenceException();
    };

    Action<int> bar = (retryCount) => Console.WriteLine($”Compensate: {retryCount}”);

    Policy
    .Handle<NullReferenceException>()
    .Retry(3, (exception, retryCount) =>
    {
    bar(retryCount);
    })
    .ExecuteAndCapture(foo);
    }

    Output SimpleRetry

    Act foo: 1
    Compensate: 1
    Act foo: 2
    Compensate: 2
    Act foo: 3
    Compensate: 3
    Act foo: 4

    ดูจาก Output SimpleRetry Act foo ครั้ง 4 ไม่มีการ Compensate ให้ ตามแผนประกันข้อผิดพลาด retry ให้ 3 ครั้ง

    Circuit-breaker: แผนประกันข้อผิดพลาดนี้จะคุ้มครองระบบไม่ให้ทำงานที่ผิดพลาดซ้ำๆหลายครั้ง พร้อมกับแก้ไขระบบให้กลับมาทำงานได้ตามปกติ มี 3 state คือ

    • Closed: เป็นสถานะเริ่มต้น กรณีการทำงานเป็นปกติ
    • Open: จะเป็นสถานะนี้ ก็ต่อเมื่อเกิดข้อผิดพลาดเกินจำนวนที่กำหนดไว้ หลักจากนั้น Circuit-breaker จะตั้งเวลาหยุดใหม่
    • (durationOfBreak) พร้อมกับ หยุดทุกกระบวนการตามระยะเวลาที่กำหนดนี้ จนกระทั่งหมดเวลา ก็จะไปอยู่ในสถานะ Half-Open
    • Half-Open: เมื่ออยู่ในสถานะนี้ Circuit-breaker จะเปิดให้ทำงานตามปกติได้ และเมื่อเรียกแล้วเกิดข้อผิดพลาดอีก ก็จะไปอยู่ในสถานะ Open อีกครั้ง วนไป แต่ถ้าเรียกกระบวนการแล้วผ่านได้ Circuit-breaker จะไปอยู่ที่สถานะ Closed ตามเดิม

    ตัวอย่าง code Circuit breaker

    public void SimpleCircuitBreaker()
    {
    int countFoo = 0;

    Action foo = () =>
    {
    ++countFoo;
    Console.WriteLine($”Act foo: {countFoo} NullReferenceException”);
    throw new NullReferenceException();
    };
    Action foo2 = () => Console.WriteLine($”Act foo2 completed”);

    var breaker = Policy
    .Handle<NullReferenceException>()
    .CircuitBreaker(
    exceptionsAllowedBeforeBreaking: 3,
    durationOfBreak: TimeSpan.FromSeconds(2),
    onBreak: (exception, durationOfBreak) =>
    {
    Console.WriteLine($”onBreak”);
    },
    onHalfOpen: () =>
    {
    Console.WriteLine($”onHalfOpen”);
    },
    onReset: () =>
    {
    Console.WriteLine($”onReset”);
    }
    );

    breaker.ExecuteAndCapture(foo); // 1
    breaker.ExecuteAndCapture(foo); // 2
    breaker.ExecuteAndCapture(foo); // 3 เปลี่ยนสถานนะ Open
    // ในช่วง 2 วินาทีรี้ ทุกกระบวนการที่เรียกหลังจากนี้จะถูกหยุด
    breaker.ExecuteAndCapture(foo); // 4 ถูกหยุด
    breaker.ExecuteAndCapture(foo2); // 5 ถูกหยุด
    // จำลองโดยการรอให้เวลาหมด 3 วินาที
    Task.Delay(TimeSpan.FromSeconds(3)).Wait();

    breaker.ExecuteAndCapture(foo); // ถูกเรียกอีกครั้ง แต่เกิดข้อผิดพลาด

    // จำลองโดยการรอให้เวลาหมด 3 วินาที
    Task.Delay(TimeSpan.FromSeconds(3)).Wait();
    breaker.ExecuteAndCapture(foo2); // เรียก function ที่ถูกต้อง
    }

    Output SimpleCircuitBreaker

    Act foo: 1 NullReferenceException
    Act foo: 2 NullReferenceException
    Act foo: 3 NullReferenceException
    onBreak
    onHalfOpen
    Act foo: 4 NullReferenceException
    onBreak
    onHalfOpen
    Act foo2 completed
    onReset

    เมื่อพิจารณาดูจะเห็นว่า Act foo เกิดข้อผิดพลาด NullReferenceException ทำงาน 3 ครั้ง แล้ว เข้าสถานะ Open คือ onBreak หลังจากนั้นแม้จะเรียก function foo ที่มี NullReferenceException หรือ foo2 ที่ไม่มี exception ก็ถูก break ไว้ในสถานะ Open

    ต่อมาจึงจำลอง โดย delay เวลาไว้ 3 วินาที มากกว่าเวลาที่ CircuitBreaker มัน break คือ 2 วินาที จนมันไปอยู่ที่สถานะ Half-Open สังเกต output onHalfOpen แล้วก็เรียก foo ให้เกิดข้อผิดพลาด จะเห็นว่าเรียกเพียงครั้งเดียวก็เข้าไปสู่สถานะ Open อีกครั้ง การนับข้อผิดผลาดจะสะสมต่ออีก จนกระทั้ง หมดเวาล break กลับไปสู่ HalfOpen อีกครั้ง จำลองให้ delay เวลาไว้ 3 วินาทีเหมือนเดิม

    ทีนี้เรียก foo2 เป็น function ที่ทำงานไม่มีข้อผิดพลาด CircuitBreaker จะกลับไปอยู่สถานะ Closed อีกครั้งพร้อมกับเริ่มนับข้อผิดพลาดใหม่

    สำหรับ กระบวนการชดเชยข้อผิดพลาด สำหรับแผนแบบ CircuitBreaker สามารถทำได้ที่ onBreak และ onHalfOpen คือเมื่อ เข้า onBreak ให้พยายาม แก้ไขข้อผิดพลาดในช่วงเวลานั้น และเมื่อเวลา break หมดลง จะเข้ากระบวนการ onHalfOpen อีกครั้งให้ตรวจสอบว่าการแก้ไขนั้นผ่านหรือยัง ก่อนที่งานในกระบวนการปกติจะกลับเข้ามาทำงานได้อีกครั้ง

    Timeout: จะประกันว่าการทำงานส่วนนี้ ต้องไม่รอนานเกินกว่าเวลา (timeout) ที่ระบุไว้แน่นอน

    แผนแบบ Timeout มี 2 ผลประโยชน์ให้เลือกคือ Pessimistic และ Optimistic

    • Optimistic Timeout: จะชดเชยก็ต่อเมื่อ เกิดข้อผิดพลาดในระหว่างการทำงานปกติ ที่เกินเวลา Timeout ไปแล้วเท่านั้น
    • Pessimistic Timeout: จะชดเชยทันทีไม่ว่ากรณีใดๆเมื่อการทำงานปกติเกิด Timeout

    ตัวอย่าง code Optimistic Timeout

    public void SimpleOptimisticTimeout()
    {
    Action foo = () =>
    {
    Task.Delay(TimeSpan.FromSeconds(3)).Wait();
    Console.WriteLine($”Throw ApplicationException”);
    throw new ApplicationException();
    Console.WriteLine($”Act foo completed”);
    };

    Action bar = () => Console.WriteLine(“Compensate”);

    var timeoutPolicy = Policy.Timeout(
    timeout: TimeSpan.FromSeconds(2),
    timeoutStrategy: TimeoutStrategy.Optimistic,
    onTimeout: (context, timespan, task, exception) =>
    {
    bar();
    }
    );

    timeoutPolicy.ExecuteAndCapture(foo);

    }

    output ของ Optimistic Timeout

    Throw ApplicationException
    Compensate

    จาก function foo ผมให้ delay งานไว้ 3 วินาที ให้มากกว่า Timeout Policy คือ 2 วินาที เมื่อ throw exception จากการทำงานปกติ หลังจากนี้จะเกิน timeout แผนความคุ้มครองก็จะชดเชยให้โดยเรียก function bar ลองปรับให้ delay งานไว้ต่ำกว่า 2 วินาทีดูครับ

    ตัวอย่าง code Pessimistic Timeout

    public void SimplePessimisticTimeout()
    {
    Action foo = () =>
    {
    Task.Delay(TimeSpan.FromSeconds(3)).Wait();
    Console.WriteLine($”Act foo completed”);
    };

    Action bar = () => Console.WriteLine(“Compensate”);

    var timeoutPolicy = Policy.Timeout(
    timeout: TimeSpan.FromSeconds(2),
    timeoutStrategy: TimeoutStrategy.Pessimistic,
    onTimeout: (context, timespan, task, exception) =>
    {
    bar();
    }
    );

    timeoutPolicy.ExecuteAndCapture(foo);

    }

    output ของ Pessimistic Timeout

    Compensate

    จาก function foo ผมให้ delay งานไว้ 3 วินาที ให้มากกว่า Timeout Policy คือ 2 วินาที จะเห็นว่า แผนนี้จะชดเชยให้ทันที โดยไม่รอให้การทำงานปกติจบลง ไม่ว่าในการทำงานจะเกิดข้อผิดพลาดใดๆก็ตาม แผนแบบ Pessimistic Timeout ก็จะชดเชยให้ครับ

    Bulkhead Isolation: แผนนี้รับประกันว่าการใช้ทรัพยกรเพื่อกระบวนการทำงานปกติต้องไม่เกินขีดจำกัดที่กำหนดไว้

    ตัวอย่าง code Bulkhead Isolation

    public void SimpleBulkheadIsolation()
    {
    Action<Context> foo = (n) =>
    {
    Task.Delay(TimeSpan.FromSeconds(1)).Wait();
    Console.WriteLine($”Act foo {n.OperationKey} completed”);
    };

    Action<string> bar = (corId) => Console.WriteLine($”Compensate for {corId}”);

    var bulkhead = Policy
    .Bulkhead(
    maxParallelization: 1,
    maxQueuingActions: 1,
    onBulkheadRejected: (ct) =>
    {
    bar(ct.OperationKey.ToString());
    });

    var taksList = new List<Task>();
    for (int i = 0; i < 5; i++)
    {
    var t = Task.Factory.StartNew(() =>
    {
    var corAppId = new Context(Guid.NewGuid().ToString());
    bulkhead.ExecuteAndCapture(foo, corAppId);
    });

    taksList.Add(t);
    }

    Task.WhenAll(taksList).Wait();
    }

    output ของ Bulkhead Isolation

    Compensate for 7b9b6042-0d36-49e3-ae1a-d6b365f6c9b8
    Compensate for d73bd757-a645-4a91-b1c7-b78cec4f64e5
    Compensate for c5b071d6-27b6-48d0-97ab-b8599ef1ec13
    Act foo 84a336ae-64c9-4cf6-a94f-6a69757cca9c completed
    Act foo c347f29f-c091-474c-a45c-5cb5cdc103a2 completed

    อธิบายตัวอย่าง code แผนแบบ Bulkhead Isolation ตัวอย่างกำหนดแผนแบบ Bulkhead กำหนดค่าดังนี้

    maxParallelization คือ ค่าจำกัดให้ทำงานพร้อมๆกัน ตัวอย่างทำได้ทีละ 1 งาน

    maxQueuingActions คือ ค่าจำกัดให้งานรอในคิวได้ไม่เกินกำหนด ตัวอย่างรอทำได้ทีละ 1 งาน

    เหตุการณ์ onBulkheadRejected จะเกิดขึ้นก็ต่อเมื่อ งานที่เข้ามาเกินค่า maxQueuingActions แล้ว ในตัวอย่างจะให้ชดเชยด้วย function bar ตัวอย่างให้เพิ่มงานวนไป 5 งาน เมื่อดู output จะเห็นว่า งานถูกชดเชยไป 3 งานและอีก 2 งานทำงานได้ตามปกติ

    Cache: แผนนี้จะคืนค่าที่รู้อยู่แล้วกลับไปทันที ถ้าไม่รู้จะเก็บค่าไว้ระยะเวลาหนึ่ง ตามที่กำหนด คือเป็นแผนที่ช่วยชดเชยผลลัพย์ได้ชั่วคราว ถูกใช้ประกอบกับแผนความคุ้มครองอื่นๆ

    Fallback: แผนนี้จะประกันว่ากระบวนการปกติต้องไม่เกิดข้อผิดพลาดใดๆ โดยจะชดเชยผลลัพย์ หรือ ทำกระบวนการให้ใหม่ แทนกระบวนการปกติ เมื่อเกิดข้อผิดพลาดขึ้นตามที่ระบุไว้ในแผน

    ตัวอย่าง code Fallback

    public void SimpleFallback()
    {
    Func<string, Product> foo = (name) =>
    {
    return new Product(name);
    };

    var fallback = Policy<Product>
    .Handle<ArgumentNullException>()
    .Fallback(() =>
    {
    return new Product(“Compensate product”);
    });

    var result = fallback.ExecuteAndCapture( () => foo(“”) );

    Console.WriteLine($”Outcome: {result.Outcome}, Product name: {result.Result.Name}”);
    }

    public class Product
    {
    public Product(string name)
    {
    if (string.IsNullOrEmpty(name))
    throw new ArgumentNullException();
    Name = name;
    }
    public string Name { get; set; }
    }

    output ของ Fallback

    Outcome: Successful, Product name: Compensate product

    อธิบายจาก code ตัวอย่าง

    ผมสร้าง class Product และให้เกิดกข้อผิดพลาดถ้าใส่ชื่อค่าว่างเข้าไป กับ กำหนด function foo ให้เป็น กระบวนการทำงานปกติ คือทำหน้าที่แค่สร้าง Product ให้ และ กำหนด fallback policy กำหนดความคุ้มครองที่ข้อผิดพลาด ArgumentNullException ซึ่งจะสร้าง Product ตั้งคือใหม่เป็น Compensate product ให้ใหม่แทนข้อผิดพลาดจาก foo

    ดู output จะเห็นว่า กระบวนการปกติตั้งใจใส่ชื่อว่าง เพื่อให้เกิดข้อผิดพลาด จะเห็นว่า Outcome นี้มีค่า Successful และ มีการชดเชย product ให้ใหม่เป็นชื่อ Compensate product

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

    ตัวอย่าง code PolicyWrap

    public void SimplePizzaResilient()
    {
    Func<string, Product> makePizza = (name) => new Product(name);

    Action<Product> normalShipPizza = (product) =>
    {
    Task.Delay(TimeSpan.FromSeconds(3)).Wait();
    Console.WriteLine($”Normal ship {product.Name} completed”);
    };

    Action<Product> specialShipPizza = (product) =>
    {
    Console.WriteLine($”Special ship {product.Name} completed”);
    };

    var makePizzaFallback = Policy<Product>
    .Handle<TimeoutException>()
    .Fallback(
    fallbackAction: (ctx) =>
    {
    // make Compensate Pizza and special ship service
    var bigPizza = makePizza(“Big Pizza by Compensate”);
    specialShipPizza(bigPizza);
    return bigPizza;
    });

    var serviceTimeout = Policy.Timeout<Product>(
    timeout: TimeSpan.FromSeconds(2),
    timeoutStrategy: TimeoutStrategy.Pessimistic,
    onTimeout: (context, timespan, task, exception) =>
    {
    // send signal for produce compensate pizza
    throw new TimeoutException();
    }
    );

    var myPizzaPolicy = Policy.Wrap<Product>(makePizzaFallback, serviceTimeout);

    myPizzaPolicy.ExecuteAndCapture(() =>
    {
    var mySinaturePizza = “Simple Pizza by #:P”;
    var thePizza = makePizza(mySinaturePizza);
    normalShipPizza(thePizza);
    return thePizza;
    });
    }

    อธิบาย code ตัวอย่างนี้เป็นการบริการพิซซ่า โดยการให้บริการครั้งหนึ่ง ประกอบด้วยกระบวนการปกติ คือ makePizza, normalShipPizza และ specialShipPizza โดย normalShipPizza ทำให้เกิด delay 3 วิทาที

    class Product ดูที่ code ตัวอย่าง Fallback

    ที่แผนความคุ้มครองการบริการพิซซ่า myPizzaPolicy คือการ wrap 2 แผนไว้ด้วยกัน ได้แก่ makePizzaFallback และ serviceTimeout โดยแผน makePizzaFallback จะทำการทำชดเชย โดยทำ pizza ขนาดใหญ่ makePizza พร้อมกับส่ง pizza แบบพิเศษ specialShipPizza

    serviceTimeout ในตัวอย่างนี้ให้ง่าย จะส่ง signal โดย throw exception TimeoutException ออกไปเฉย เพื่อให้ makePizzaFallback ทำกระบวนการชดเชยให้

    พอลอง run ดูก็จะเห็น output เป็นแบบนี้ครับ

    Special ship Big Pizza by Compensate completed

    แล้วลองแก้ไข delay ใน function normalShipPizza เป็น 1 วินาทีดู ก็จะ output ปกติออกมา ลอง run ดูครับ

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

    ขอบคุณครับ

    #:P

  • API Gateway ด้วย ASP.NET Core Web API ภายใน 15 นาที

    สวัสดีครับ บล็อคนี้ผมขอเอาแบบเร็วๆนะครับ

    API Gateway เป็น services ตัวหนึ่งที่ทำหน้าที่รับ request จากผู้ใช้ แล้ว ส่งต่อ request ไปยัง service providers พอได้ response กลับมา ก็ส่ง response กลับไปยังผู้ใช้

    ประโยชน์

    1. ควบคุมจัดการการเข้าใช้ WEB API ที่จุดเดียว
    2. ทำ load-balance, fail-over WEB API ได้
    3. ควบคุมจัดการ version ของ WEB API โดยไม่ต้องเปลี่ยนแปลงที่ client

    ข้อเสีย

    API Gateway server เป็นด่านแรกเพื่อเข้าถึงทุกบริการ ดังนั้นถ้ามันตายไป ก็คือทั้งระบบเสีย การแก้ก็คือ ให้เราสร้าง cluster สำหรับ API Gateway server อีกที

    สิ่งที่ต้องการ Visual Studio 2017 Community

    ตัวอย่างนี้ สร้าง 2 components คือ

    1. APIGateway ชนิด project เป็น ASP.NET Core Web Application เป็น Empty project
    2. SimpleValuesServices ชนิด project เป็น ASP.NET Core Web Application เป็น API

    1. สร้าง APIGateway

    • เปิด Visual studio สร้าง project APIGateway เลือก ASP.NET Core Web Appliation เลือกเป็น Empaty

    • ติดตั้ง Ocelot ไปที่ Package Manager Console พิมพ์คำสั่ง
      • PM> install-package Ocelot
    • เพิ่มไฟล์ routes.json (เลือก properties เป็น Copy always) ให้กำหนด Downstream, Upstream ของ HOST ตามนี้

     

    • เปิด class file Startup.cs เขียน code แบบข้างล่างนี้ลงไป

    • เปิด class file Program.cs เขียน code แบบข้างล่างนี้ลงไป

    • code เสร็จแล้วครับ build แล้ว run โดยเข้า command cd ไปที่ folder APIGateway\bin\Debug\netcoreapp2.1 จะมี APIGateway.dll อยู่ให้ พิมพ์ command ตามข้างล่างนี้

    2. สร้าง SimpleValuesServices

    • เพิ่ม project เหมือน APIGateway แต่ให้เลือกเป็น API ตามรูป

    • ไปที่ class file Program.cs เพิ่ม code ตามข้างล่างนี้ลงไป

    • เสร้จแล้วครับ ให้ run 2 service เป็น cluster กัน โดยเปิด command แล้ว cd ไปที่ folder SimpleValuesServices\bin\Debug\netcoreapp2.1 จะมี file SimpleValuesServices.dll
      • run SimpleValuesServices port 5001
      •  run SimpleValuesServices port 5002

    เอาละทุกอย่างเสร็จหมดแล้ว ทีนี้มาทดสอบกันครับ เปิด chrome ขึ้นมา พิมพ์ไปที่ http://localhost:9000/values

    มันจะ ส่ง request สลับวนไปตาม algorithm ที่เรากำหนดไว้ (ตัวอย่างนี้กำหนดแบบ RoundRobin) ไป ยัง url simple values services สองตัวที่เปิดอยู่คือ http://localhost:5001/api/values กับ http://localhost:5002/api/values ครับ

    Software Architecture ตัวอย่างเป็นแบบภาพนี้

     

    แหล่งข้อมูล

    1. https://ocelot.readthedocs.io/en/latest/features/loadbalancer.html#id6
    2. API Gateway pattern

     

  • อธิบายการพัฒนาซอฟแวร์แบบส่งมอบของก่อน ด้วยตัวอย่าง (Delivery-first by Example)

    สวัสดีครับ

    บล็อคนี้ผมเขียนต่อจากบล็อคที่แล้ว การพัฒนาซอฟแวร์แบบส่งมอบของก่อน (Delivery-first)

    การพัฒนาซอฟแวร์แบบส่งมอบของก่อน แบ่งช่วงขั้นตอน (Phase) การทำงานออกเป็น 3 ช่วง คือ Delivery, Detect และ Defect หรือ 3D

    ช่วง Delivery เป้าหมายเพื่อส่งมอบของ (Deploy) โดยการส่งมอบนี้ไม่จำกัด อาจจะเป็นเพื่อส่งให้ลูกค้าใช้เลย (Production) หรือ ส่งเพื่อให้ลูกค้าทดสอบ (Acceptance / Functional test) หรือ ส่งมอบเพื่อทดสอบร่วมกันกับทีมพัฒนา (Integration test) หรือ ทดสอบเฉพาะส่วนเดี่ยวๆก็ได้ (Unit-test) ผมรวมเรียกว่าการส่งมอบ คือ Delivery หมายถึง ส่งมอบของที่ใช้ประโยชน์ได้ ทำงานได้จริง มีผลลัพธ์ออกมา ไม่ใช่แค่ code หรือ ไอเดีย แนวคิด การออกแบบ หรือ เอกสาร ต่างๆ

    ช่วง Detect เป้าหมายเพื่อ ตรวจจับ ให้ได้ค่าที่ประเมิณผล (Measure) ได้จริง หรือก็คือการใช้ Fitness function เพื่อตรวจสอบว่าผลลัพธ์ทั้งหมดเราที่ประเมิณผลได้จากซอฟแวร์ที่ส่งมอบไปนั้นบรรลุเป้าหมายของเราหรือไม่ ผลสรุปจาก detect นี้ทำให้ทีมพัฒนา หรือ ผู้ใช้ และผู้ที่เกี่ยวข้อง เห็นชัดเจนว่าผลลัพธ์นี้ถูกต้อง ถูกใจเป็นไปตามความต้องการหรือไม่ ซึ่งก็คือรายการ defect เพื่อใช้ดำเนินการในขั้นตอน Defect ต่อไป

    ช่วง Defect เป้าหมายเพื่อพิจารณาตัดสินใจ defect ต่างๆจากที่เรา detect ได้ว่า จะแก้ไข ปรับปรุงของ หรือ ซอฟแวร์ที่เราส่งมอบไปแล้วนั้นต่อไป หรือไม่ แล้ววนกลับไปสู่ช่วง Delivery ต่อไป ขั้นนี้ก็คือ  Continuous improvement ซึ่งเป็นกระบวนการบังคับในรูปแบบการพัฒนาซอฟแวร์เชิงวิวัฒนาการนั่นเอง

    ตัวอย่าง ผมจะเป็นงานบัญชีสร้างรายงานสมุดรายวันทั่วไป ผลลัพย์แสดงเป็น html ง่ายๆครับ ขั้นตอนพัฒนาตามนี้เลยครับ

    ต้องการ Visual studio 2017 community edit ครับ Download และติดตั้งฟรีได้ที่ VS2017

    Delivery 

    1. ผลลัพธ์เป็นรายงาน HTML ครับ ชื่อ file report.html หน้าตาตามที่ออกมาเป็นแบบนี้
    2. สร้าง projects ภาษา C# ชื่อว่า Accounting.Core เป็น library สำหรับงานบันทึกธุรกรรมทางบัญชี 
    3. ใน projects ผมได้สร้าง class 3 class และ 2 enumeration type เพื่อใช้แทนงานทางบัญชี ซึ่งตอนแรก ก็ไม่ใช่ 3 class นี้ แต่ไม่เป็นไร ให้ทำไปก่อนครับ ตามที่เราเข้าใจความต้องการในตอนนั้น ไปได้เลย 3 class มีดังนี้
      1. class Account : สมุดบัญชีบันทึกรายวันทั่วไป
      2. class AccountEntry : รายการบัญชีแบบสองด้าน
      3. class AccountTransaction : ธุรกรรมธุรกิจ เพื่อใช้ระบุได้ว่า แต่ละรายการที่เปลี่ยนแปลงไปนั้นเป็นชุดธุรกรรมเดียวกัน
      4. enum AccountEntryType : ชนิดของการลงบันทึกบัญชีสองด้าน มีสองค่าคือ Debit และ Credit
      5. enum AccountType : ประเภทของรายการบัญชี แต่ละประเภทแบ่งเป็น 3 กลุ่มได้แก่ สินทรัพย์ (Assets), หนีสิน (Liabilities) และ ส่วนของเจ้าของ (Shareholders’ Equity)
    4. code ส่วนของของที่พร้อมส่งมอบแล้วอยู่ที่ Accounting.Core
    5. หลังจาก build เสร็จจะได้ของเป็น library file Accounting.Core.dll พร้อมส่งมอบให้ผู้ใช้ใช้งานได้ โดยผมจะสร้าง projects เพื่อแทนการใช้งาน library นี้อีกหนึ่ง project ชื่อว่า Accounting.Core.Client โดยเป็นแบบ MSTest ครับ คือมันเป็น xUnit สำหรับ run ใช้ของ Accounting.Core.dll ของผม แทนผู้ใช้เป็นตัวอย่างไปก่อน
    6. ใน project Accounting.Core.Client เพิ่ม Test class Delivery เพื่อใช้แสดงการใช้ของ คือ Accounting.Core.dll ครับ โดยการ add reference เข้ามา แล้วเพิ่ม Test method เพื่อดำเนินการเพิ่มธุรกรรมทางธุรกิจเพื่อให้มีรายการบันทึกบัญชี ไว้ออก report ได้ มีรายการธุรกกรรมทั้งหมด 4 รายการ คือ
      1. ธุรกรรมการค้า รหัส 001 – ขายสินค้าให้นาย ก. 1 ชิ้น รับเงินสด 1000 บาท
      2. ธุรกรรมการค้า รหัส 002 – จ่ายสินค้าคงเหลือเพื่อขาย นาย ก. 1 ชิ้น 800 บาท
      3. ธุรกรรมการค้า รหัส 003 – ขายสินค้าให้นาย ข. 2 ชิ้น รับเงินโอน PromptPay 2000 บาท
      4. ธุรกรรมการค้า รหัส 004 – จ่ายสินค้าขายนาย ข. 2 ชิ้น 1600 บาท
      5. หลังจากบันทึกรายการธุรกรรมการค้าเสร็จ ผมสั่ง build ได้ report.html ออกมาไปวางที่ path results ครับ
    7. Code client ทั้งหมด ดูได้ที่ Accounting.Core.Client.Delivery

    หลังจากที่มีการใช้งานของแล้ว ขั้นตอนต่อมาก็คือการ Detect ครับ

    Detect

    1. เพื่อไม่ให้เสียเวลา ผมใช้ Project client เพื่อเขียน script ในการ detect ผลลัพธ์จากการใช้งานแล้วครับ เพิ่ม Test class Detect เขาไป
    2. เนื่องจาก file ที่เราต้อง Detect นั้นเป็น html ผมขอติดตั้ง lib ตัวช่วยเข้าไปหน่อยคือ HtmlAgilityPack ครับลงผ่าน nuget ได้เลยครับ
      1. PM> install-package HtmlAgilityPack
    3. เพิ่ม Test method watch_sell_goods_and_build_generalJournal_report  เข้าไปเพื่อช่วย run script Detect ผลลัพย์จาก Client ครับ ชือว่า ดู code ได้ที่ Accounting.Core.Client.Detect
    4. หลังจากนั้นให้ run Test method โดยการ click mouse ขวา แล้วเลือก Run test ครับจะเห็นผลลัพธ์ที่ detect ได้เลย เพราะผมใช้ MSTest มันจะแสดง report ออกมาว่าผ่าน ไม่ผ่านตามที่เราเขียนไว้ 
    5. ดูผลลัพย์ detect จากหน้าต่าง Test Explorer ด้านซ้ายได้เลยครับ 

    จะเห็นว่าทดสอบผ่านหมดครับ เพราะขึ้นอยู่กับเราจะเขียนให้ code detect ผลลัพย์ให้เป็นอย่างที่คาดหวังขนาดไหน ตัวอย่างนี้ก็เขียนให้ง่ายไว้ก่อน แค่เช็คว่ามี file report.html หรือไม่ และ ตรวจสอบยอดบันทึกด้าน Debit และ Credit ว่าเท่ากันรึเปล่า แค่นั้น

    สำหรับขั้นตอน Defect ก็รวมอยู่ใน Detect แล้วครับ มันก็คือเราดูผลที่ Detect ได้จากนั้นเราก็ตัดสินใจว่าจะ แก้ไขซอฟแวร์ของเราหรือไม่ ขั้นตอนนี้เรียกว่า Defect

    หวังว่านักพัฒนาจะเข้าใจรูปแบบการแก้ปัญหาแบบ การพัฒนาซอฟแวร์แบบส่งมอบของก่อน หรือ Delivery-first ได้พอเข้าใจบ้าง จริงๆแล้วมันมาจากการเรียนรู้ที่เราไม่เข้าใจว่าจะทดสอบอย่างไร แต่เรารู้แล้วว่าจะแก้ปัญหาอย่างไรก่อน แล้วจึงมาตรวจสอบให้ได้ว่า ผลลัพย์จากการแก้ปัญหาของเรานั้นมันถูกต้อง เป็นที่น่าพอใจรึยัง แล้วก็กลับไปแก้ไขวิธีการแก้ปัญหาของเราให้ได้ผลลัพย์ออกมาจนกระทั่งเราพอแล้ว แค่นั้นครับ

    ปัญหาที่ผมเจอคือ บางครั้งเราก็หาคำตอบได้มาทดสอบ ใช้เขียนทดสอบได้ตั้งแต่ต้น เรียกว่า Test-Driven Development (TDD) แต่บางงานเรายังไม่รู้เลยว่าคำตอบควรจะเป็นอย่างไร แต่เรารู้แล้วว่าผลลัพธ์มันจะออกมาแบบนี้ แล้วผมก็จึงสืบค้นหาคำตอบว่าผลลัพย์มันต้องออกมาเป็นแบบไหน จึงได้เกิด Delivery-first แล้วผมก็เรียกมันใหม่ว่า Delivery-driven Development (DdD) ต่อจากนี้ไปครับ

    ขอบคุณครับ

    #:P

  • การพัฒนาซอฟแวร์แบบส่งมอบของก่อน (Delivery-first)

    สวัสดีครับ

    บล็อคนี้ผมจะมาแชร์ เทคนิคการพัฒนาซอฟแวร์ ที่ผมทำแล้วง่ายกว่า เร็วกว่า และได้คุณภาพเหมือนกับ Test-Driven-Development (TDD) ที่เราๆนิยมใช้กัน

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

    จนมาวันหนึ่งผมไม่ได้เขียน Test ก่อนเลย พร้อมทั้งได้งานที่ Refactor code ของระบบเก่าหลายโปรเจ็ค ผมก็ได้ใช้เทคนิคนี้มาตลอด แต่ผมก็ไม่ได้เรียกมันสักที แต่มันไม่ใช่ TDD แน่นอน แต่ก็แนะนำให้นักพัฒนาใช้ TDD ก่อน ก่อนที่จะเข้าใจว่าไม่ต้องเขียน test ก่อนพัฒนาเป็นอย่างไร จนได้เกิดเทคนิคนี้ขึ้น

    เทคนิคนี้คือว่าไม่ได้เขียน test ก่อน แต่งานที่ผมทำก็ออกมามีคุณภาพ และรวดเร็ว เหมือนกับ TDD ครับ ทั้งงานสร้างใหม่ และงาน refactor ของเดิม

    ผมจึงตั้งชื่อเทคนิคนี้เองว่า Delivery-first หรือ ส่งมอบของก่อน ของในที่นี้ เป็นซอฟแวร์

    เทคนิค ส่งมอบของก่อน ขั้นตอนก็แสนง่ายครับ มี แค่ 3 ขั้นตอน คือ Delivery-Detect-Defect หรือเรียกสั้นๆว่า 3D ครับ ไม่ว่าคุณจะมีของอยู่แล้ว หรือ สร้างของขึ้นมาใหม่ ใช้เทคนิคนี้ได้หมดครับ ต่างจาก TDD คือ ไม่ต้องเขียน test ก่อน

    ขั้นตอนแรก Delivery คือ ลงมือสร้างของนั้นขึ้นมาเลยครับ ถ้าพร้อมส่งมอบก็ส่งได้เลย ถ้าไม่พร้อม ก็ deploy ขึ้นทดสอบก่อน ไม่ต้องเขียน test เขียน code ขึ้นมาก่อน build แล้ว delivery เลย หรือถ้ามีของอยู่แล้วก็ข้ามไปขั้นที่สองครับ

    ขั้นตอนที่สอง Detect คือ ให้เขียน code  เพื่อตรวจจับผลลัพธ์ของสำเร็จที่เราส่งมอบไปจากขั้นแรก แล้วเอาแสดงใหเได้ หรือถ้ามีข้อมูลมาเปรียบเทียบผลลัพธ์ที่เราคาดว่ามันต้องเป็น ก็เอามาสรุปรวมกันไปครับ

    และ

    ขั้นตอนที่สาม Defect คือ ดูจากผลสรุปที่ได้จาก ขั้นตอน Detect แล้วผลลัพธ์ได้ตามคาดหวัง หรือไม่ได้ หรือว่าเราพอใจ ไม่พอใจแล้ว ก็ให้เรากลับไปแก้ไขการสร้างของ ของเราให้ถูกต้องตรงใจได้เลยครับคือ ย้อนกลับไปที่ขั้นตอน Delivery

    ตัวอย่าง-1: ซอฟแวร์ feature ง่ายๆอย่างการ login

    1. Delivery – ผมมีหน้า login อยู่แล้ว ผมก็เข้าไปเล่นได้เลยโดย login username/password ปกติ ถ้าไม่มีก็สร้างมันขึ้นมา แล้ว deploy ให้มีหน้าจอสำหรับ login ใส่ username/password ขึ้นมาเลย ผลลัพธ์คือ ผม login ผ่านเข้าไปเจอหน้าแรกได้
    2. Detect – ผมเขียน automate script สำหรับอ่านข้อมูลการ login ของผมขึ้นมา แล้วดูว่า login ได้หรือไม่ แล้วแสดงเป็นรายงานออกมาให้เห็นได้
    3. Defect- ผมก็ตรวจดูผลลัพธ์จากรายงาน Detect ถ้าได้ผลว่าผม login อยู่ ก็แสดงว่าการส่งมอบของนั้นผ่านครับ

    ตัวอย่าง-2: สร้างโครงการใหม่เลยครับคือ บันทึกข้อมูลสินค้าสักอย่างหนึ่ง

    1. Delivery – ผมสร้างหน้าจอบันทึกสินค้าที่สามารถบันทึกสินค้าได้จริงๆ แล้ว deploy สำหรับทดสอบก่อนเลยครับ อย่าเขียน test นะครับ
    2. Detect – ผมเขียน automate script เพื่ออ่านข้อมูลที่บันทึกลงไป แล้วสรุปแสดงผลออกมา ให้เห็นอ่านเข้าใจว่ามีข้อมูลที่ บันทึกลงระบบได้จริงๆ
    3. Defect – ก็ตรวจดูผลลัพธ์จาก Detect ครับว่าถูกต้องมั้ย ถ้าเราอยู่ในขั้นตอน test เราก็มีผลที่คาดหวังสรุปออกมาที่ Detect ได้เลยครับ แล้วก็ดำเนินการแก้ไขกลับไปขั้นตอน Delivery ต่อไป

    ขอนำเสนอแค่นี้ก่อนครับ ผมจะเขียน code ให้ดูต่อไป ผมใช้เทคนิคนี้แล้วมันง่ายมาก ไม่ต้องมากังวลว่าของจะไม่เสร็จ และไม่มีคุณภาพครับ เพราะมันเสร็จตั้งแต่ต้น รวมถึงมี Detect เพื่อตรวจดูแล้วแจ้ง Defect กลับมาให้เรากลับไปแก้ไขของที่ Delivery ไปแล้วอยู่ตลอดเวลาเลยครับ

    * เพิ่มเติมอ่านคำอธิบาย Delivery-first ด้วยตัวอย่าง ได้ที่ อธิบายการพัฒนาซอฟแวร์แบบส่งมอบของก่อน ด้วยตัวอย่าง (Delivery-first by Example)

    ขอบคุณครับ

    #:P

     

  • งาน, กำลัง, ความซับซ้อน และ Story points เพื่อการประมาณ และ วางแผนงาน แบบ Agile

    สวัสดีครับ

    วันนี้ผมจะมาอธิบายการประมาณค่า เพื่อ วางแผนงานแบบ Agile ที่เรานิยมใช้กัน ก็คือ Story points (SP)

    SP ก็คือ ขนาดของงาน หรือ ความต้องการ มันก็คือขนาดของซอฟแวร์ที่เรากำลังพัฒนา

    เราใช้ SP เพื่อไปวางแผนปล่อยซอฟแวร์, คำนวนต้นทุนค่าแรงพัฒนาซอฟแวร์ และ ติดตามงานเพื่อให้อยู่ในแผนงานที่เรากำลังทำอยู่

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

    ส่วนความสามารถของคน หรือ ทีมที่จะทำงานนี้ให้เสร็จนั้น เรานิยมวัดด้วยขนาดของเวลาที่ใช้ทำงานนั้นจนเสร็จ เรียกว่า ความพยายาม (effort)

    ในการอธิบายเรื่อง SP นี้ที่เราพบเจอกันบ่อย เขามักจะอธิบายว่า มันคือค่า ความพยายาม กับ ความซับซ้อนของงาน ซึ่งกำหนดแบบนี้ก็ไม่ผิด เดี๋ยวผมจะอธิบายต่อไปว่า SP, ความพยายาม และ ความซับซ้อนของงาน มันแตกต่างกันอย่างไร แล้วความแตกต่างกันตรงนี้ทำให้ SP นั้นเกิดความผิดพลาด (error) ในการวางแผนงานกันในทีมได้อย่างไร

    ทำไม SP ถึงถูกอธิบายเป็นแบบนั้น ผมขออธิบายด้วย สมการงาน ครับ สมการงาน กำหนดได้แบบนี้

    Work(W) = Force(F) x Distance(d)

    Work ก็คือ งาน ซึ่งเทียบได้กับ ความต้องการ หรือ คุณสมบัติของซอฟแวร์ที่ต้องการ (feature) หรือ issue

    Force ก็คือ กำลัง ซึ่งเทียบได้กับ ความพยายาม ที่คนในทีมใช้ทำงานนั้นจนสำเร็จ และ

    Distance ก็คือ ระยะทาง ซึ่งเทียบได้กับขนาดของงาน หรือ SP

    ตัวอย่าง เมื่อเราประมาณงานหนึ่งชิ้น (Work item) สมมิว่า คือ w1

    เรามีทีมพัฒนาที่มีสมาชิกอยู่ 2 คน คือ p1 และ p2 ให้ทั้งสองคนประมาณงาน w1 นี้

    p1 ประมาณ ใช้เวลา 1 วัน และ p2 ประมาณ ใช้เวลา 2 วัน

    ถ้าเราใช้ กำลังเป็นหน่วยวัดของงานแล้ว เราจะพบว่า งานแบบเดียวกันขนาดมันไม่มีทางเท่ากันได้เลย เป็นเพราะอะไร

    แทนค่าประมาณงานของทั้งสองคนลงไป ในสมการงาน เราจะได้

    w1 = F * 1 — สำหรับ p1

    w1 = F * 2 — สำหรับ p2

    เราจะพบว่า การทำให้ w1 เหมือนกันให้ได้นั้น p1 ต้องออกแรง (F) มากกว่า p2 เป็น 2 เท่า แต่การที่เราเอาเวลา มาเป็นตัววัดขนาดงาน ให้แต่ละคนในทีมเห็นเหมือนกันนั้นเป็นไปไม่ได้เลย ดังนั้น จึงไม่แปลกว่าทำไมการจัดการวางแผนมีความผิดพลาด (error) อยู่เสมอ เช่น ปัญหาในการวางแผนงานโดยประมาณแบบผู้เชี่ยวชาญ ว่างานชิ้นนี้ คนนี้ใช้ 1 วันทำเสร็จ ดังนั้น งานชิ้นนี้ให้ใครทำก็จะต้อง 1 วันเสร็จเหมือนกัน ซึ่งเราจะเห็นว่า มันเป็นไปไม่ได้ในทุกกรณี หรือ ความเข้าใจที่ว่า งานชิ้นนี้คนเดียวทำ 1 วัน ถ้าเราใช้ 2 คนก็ต้องเสร็จได้ภายในครึ่งวันแน่นอน ความเข้าใจทั้งหมดนี้เกิดมากจาก การใช้ Effort กับ Complexity ประมาณขนาดของงาน ซึ่งทั้งสองค่านี้มีความสัมพันธ์กับทีมพัฒนาแต่ละคน ผมก็จะอธิบายในลำดับต่อไป

    ดังนั้การจัดการแบบ Agile จึงเลือกใช้ SP มาเป็นตัวประมาณค่าขนาดของงาน เพื่อนำมาวางแผนงานทำงานต่อไป นั่นเอง

    เทคนิคที่นิยมใช้ประมาณขนาดของงาน เช่น Planning poker, Swimlane Sizing

    พอเราได้ขนาดมาแล้ว เราก็ให้ทีม มาประมาณกำลัง หรือเวลาทำงานชิ้นนั้นอีกที

    ทีนี้มาถึงความซับซ้อนของงาน ทำไมมีหลายๆคน อธิบายว่า SP คือ ความพยายาม กับ ความซับซ้อน ได้

    ผมจะยกตัวอย่าง งานเหมือนเดิม ในทีมพัฒนา มีสมาชิก 2 คนคือ p1 และ p2 ให้ประมาณงาน w1 เหมือนกัน ได้ดังนี้

    p1 ประมาณงาน w1 ได้ 1 วัน และ p2 ก็ประมาณงาน w1 ได้ 2 วัน

    w1 = F * 1 — สำหรับ p1

    w1 = F * 2 — สำหรับ p2

    จากสมการงาน เรารู้แล้วว่า F ของ p1 ไม่เท่ากับ p2 แต่ทำไมถึงไม่เท่ากัน และทำอย่างไรถึงจะให้เท่ากันได้ เพื่อให้เห็นขนาดของงาน w1 นี้ตรงกัน

    ทีนี้ผมจะพยายามทำให้ ทั้งสองคนนี้ใช้กำลังเท่ากัน เพื่อใช้อธิบายเรื่องความซับซ้อนของงาน โดยเลือกเอาค่าน้อยที่สุด เพื่อให้งานเสร็จไวครับ ก็คือ 1 วัน

    ถ้า ผมให้ F เท่ากับ Effort (e) คือ ความพยายาม คูณกับ Knowledge (k) ความรู้ในงาน จะได้สมการงานใหม่ เป็น

    F = k x e

    นำไปแทนค่าใน สมการงาน ได้เป็น

    W = k x e x d

    โดยที่ k มีค่าระหว่าง 0 ถึง 1 คือ

    ถ้าค่า k เท่ากับ 0 ไม่รู้เรื่องเกี่ยวกับงานนั้นเลย, ถ้าค่า k เท่ากับ 1 คือมีความรู้ในงานนั้นอย่างสมบูรณ์ ถ้าค่า k อยู่ระหว่าง 0 กับ 1 ก็คือ มีความรู้ในงานตามส่วนไป เช่น 0.5 หรือ รู้ 50% เป็นต้น (ซึ่ง 1-k มีความสัมพันธ์ทางบวกกับ technical debt )

    ถ้าผมให้ Complexity (c) หรือ ความซับซ้อน คือความไม่รู้เกี่ยวกับงาน ดังนั้น c จะเท่ากับ 1 – k นั่นเอง

    ดังนั้น ถ้าผมให้ p1 มี k เท่ากับ 1 คือ มีความรู้สมบูรณ์ และ p2 มี k เท่ากับ 0.5 หรือรู้ในงานนี้ 50% แทนค่าในสมการงาน ได้เป็น

    w1 = e * 1 * 1 — สำหรับ p1

    w1 = e * 0.5 * 2 — สำหรับ p2

    เราจะพบว่าสมการงาน มีค่าเท่ากันได้ โดย p1 และ p2 ในความพยายาม (e) เท่ากัน แต่ความรู้ของทั้ง p1 และ p2 ไม่เท่ากัน

    และนี่ก็คือที่มาว่า ทำไมถึงมีคนอธิบายว่า Story points ที่ใช้เป็นหน่วยวัดงาน นั้น มันก็คือ ความพยายาม (e) กับ ความซับซ้อน (c) เพราะว่า พวกมันมีความสัมพันธ์กัน แต่ไม่เกี่ยวข้องกัน แต่ส่งผลทำให้เกิดงาน นั้นเอง

    ก่อนไป ผมมีอีกสามาการหนึ่งที่สามารถนำมาใช้อธิบายความสัมพันธ์ระหว่าง งาน, กำลัง, ความซับซ้อน และ Story points ก็คือ สมการมูลค่า ที่ว่า

    Value = Price x Quantity

    ผมเทียบกับงานซอฟแวร์ แล้วอธิบายได้ว่า

    Price เท่ากับ Effort x Knowledge และ Quantity ก็คือ Story points ก็ได้เช่นเดียวกับ สมการงาน ครับ

    ผมก็ขออธิบายไว้เพียงเท่านี้ หวังว่าจะช่วยให้เข้าใจ Story points เพื่อใช้ในประมาณค่า เพื่อวางแผนงาน ที่เข้าใจได้ชัดเจนขึ้นต่อไปครับ

    ขอบคุณครับ

    #:P

    หนังสือแนะนำอ่านเพิ่มเติม

     

     

Design a site like this with WordPress.com
Get started