ปล. ก่อนหน้านี้ ใช้ได้เฉพาะ Docker Enterprise และ Mac เท่านั้น ตอนนี้ทุกคนสามารถใช้ได้หมด พร้อมเปิดเป็น open source อีกด้วย
มาใช้งาน Docker Compose บน Kubernetes กัน
บันทึกการเดินทาง Microservices Journey
- ง่ายต่อการพัฒนา
- ง่ายต่อการเปลี่ยนแปลง
- ง่ายต่อการทดสอบ
- ง่ายต่อการ deploy
- ง่ายต่อการ scale
Microservice คืออะไร ?
เป็นเทคนิคหนึ่งในการพัฒนาระบบงาน เพื่อแยกส่วนการทำงานออกเป็น service ย่อย ๆ ที่มีขนาดเล็ก (คำถามคือ อะไรคือคำว่าเล็ก ?) แต่ละ service มีการทำงานเพียงอย่างเดียว (Single Responsibility) แต่ละ service ต้องให้ทำงานจบในตัวเอง (Self service) นั่นคือมี data store หรือที่จัดเก็บข้อมูลของแต่ละ service แต่ถ้าต้องเรียกหรือทำงานร่วมกับ service อื่น ๆ ต้องไม่ลึกเกิน 3 ชั้น (อ้างอิงจาก Service Principle) เนื่องจากจะทำให้ service มีความซับซ้อนเกินไป ที่สำคัญการทำงานร่วมกับ service อื่น ๆ ทำให้เกิดปัญหาตามมามากมาย ที่สำคัญขัดแย้งกับแนวคิดข้างต้น ผลที่ได้คือ Service ง่ายต่อการทำความเข้าใจ Service ง่ายต่อการพัฒนา Service ง่ายต่อการทดสอบ Service ง่ายต่อการ deploy Service ง่ายต่อการ scale ที่สำคัญของการแยกเป็น service เล็ก ๆ คือ ทำให้แต่ละทีมสามารถพัฒนา Service ไปพร้อม ๆ กันได้ รวมทั้งแต่ละทีมพัฒนา service สามารถทำได้ตั้งแต่ พัฒนา ทดสอบและ deploy เองได้ (Autonomous Team) นั่นคือแต่ละทีมสามารถปิดงานได้อย่างอิสระ แน่นอนว่า มันส่งผลต่อโครงสร้างและขั้นตอนการทำงานของทีมและบริษัทอย่างแน่นอน ลองคิดดูสิว่า ถ้าขั้นตอนการทำงานยังซับซ้อน มากมาย ล่าช้าแล้ว แต่นำ Microservice มาใช้งาน มันจะรอดไหม ?แต่ว่าเราจะแยก service ในแนวทางของ Microservice ทำได้อย่างไรบ้าง ?
วิธีง่าย ๆ แบ่งออกเป็นสองมุมมองคือ 1. มุมมองทาง business (Business capability) 2. มุมมองทางด้วย technical (Technical capability) ในระบบส่วนใหญ่นั้น เรามักจะแบ่งส่วนการทำงานต่าง ๆ เป็นส่วนเล็ก ๆ อยู่แล้ว แต่แบ่งเป็น layer ตามหน้าที่การทำงาน ยกตัวอย่างเช่น User Interface, Business layer และ Database layer เป็นต้น ส่งผลให้จำนวนทีมก็แบ่งตาม layer เหล่านี้นั่นเอง ผลที่ตามมาจากการแยกทีมทำงานตามแต่ละ layer คือ การทำงานร่วมกันยากขึ้น ทั้งการพูดคุยกัน ทั้งการวางแผน เพื่อให้ไปในทิศทางเดียวกัน รวมทั้งการประเมินค่าใช้จ่ายที่มักจะสูงอีกด้วยยิ่งหน่วยงานใหญ่มากขึ้นเท่าไร จำนวน layer และจำนวนทีมก็สูงขึ้น ดังนั้น overhead ของการทำงานยิ่งสูงขึ้นดังนั้นระบบงานของเราเป็นอย่างไร มันบ่งบอกว่าโครงสร้างขององค์ของเราเป็นเช่นนั้น ตามกฏของ Conway แสดงดังรูป ดังนั้นด้วยแนวคิด Microservice จึงไม่ทำตามวิธีการข้างต้น เพราะว่า สิ่งที่ผิดพลาดไปแล้ว ไม่น่าจะทำตามใช่ไหม ? นั่นคือ ทีมทำงานจะมีคนที่มีความสามารถครบตามที่ระบบงานต้องการ ยกตัวอย่างเช่น ต้องมี UI, Middleware และ Database Administrator specialist กลายว่าทีมจะมีรูปแบบที่เรียกว่า Cross-functional team แสดงดังรูป
คำถามต่อมาคือ ขนาดของ service จะมีขนาดเล็ก มันต้องเล็กเพียงใด ?
จากข้างต้นบอกว่า แต่ละ service ต้องมีทีมที่ดูแล เรียกว่า Cross-functional team ดังนั้นขนาดของ service จะใหญ่เพียงใดนั้น ตอบได้ง่าย ๆ คือ ทีมนั้น ๆ สามารถดูแล service ได้หรือไม่ ? (You build it, You run it) ระบบงานที่เราสร้างมานั้น ไม่ได้เน้นไปที่จำนวน feature ให้ใช้งาน แต่เน้นไปที่คุณค่าของระบบงานที่ให้ทางผู้ใช้งานและ business รวมทั้งขนาดของ service ที่เล็ก จะยิ่งช่วยทำให้ ทีมพัฒนาและผู้ใช้งาน รวมทั้ง business ใกล้ชิดกันมากขึ้น ซึ่งมันส่งผลดีต่อทุกฝ่ายส่วนเรื่องของจำนวนบรรทัดของ code ไม่ได้เกี่ยวกับ Microservice เลย !!
เนื่องจากแต่ละ service นั้นต้องมี data store หรือที่จะเก็บข้อมูลเป็นของตัวเอง
มันทำให้เกิดคำถามหนึ่งขึ้นมาคือ เราจัดการความถูกต้องของข้อมูลกันอย่างไร ถ้าข้อมูลหล่านั้นถูกใช้งานข้าม service ? ปล. ปัญหานี้จะไม่เกิดในระบบ Monolithic อย่างแน่นอน แสดงดังรูป วิธีการจัดการปัญหาเรื่อง ความถูกต้องของข้อมูล (Data consistency) มีดังนี้- การ aggregate ข้อมูลจาก data store จาก service ต่าง ๆมารวมกัน
- Domain event คือเมื่อมีการเปลี่ยนแปลงใด ๆ แล้วจะทำการส่งการเปลี่ยนแปลงนั้นไปยังส่วนกลางที่จัดการระบบทั้งหมด
- Event sourcing
- Saga
ถ้าแต่ละ service ต้องดึงข้อมูลข้าม service จะต้องทำอย่างไร ?
วิธีการจัดการมีดังนี้- API composition
- CQRS (Command Query Responsibility Segregation)
แน่นอนว่า การจัดการปัญหาเรื่องนี้ไม่ได้ง่ายเลย ดังนั้นเราต้องเข้าใจข้อดีและข้อเสียของวิธีการต่าง ๆ เพื่อให้เรารู้และสามารถคิดวิธีการรับมือไว้รอ ไม่มีคำว่า งานงอก อีกต่อไป
จะพบว่าจำนวน Service เยอะมาก ๆ แล้วแต่ละ service จะติดต่อสื่อสารกันอย่างไร ?
การสื่อสารนั้นมีรูปแบบหลัก ๆ 2 แบบคือ- Synchronous หรือ Request-Response Model คือต้องทำการรอผลลัพธ์จาก request ที่ส่งไปยังผู้ให้บริการ
- Asynchronous จะตรงข้ามกับ Synchronous นั่นคือไม่ต้องรอนั่นเอง ทำให้สามารถติดต่อในรูปแบบ one-to-one และแบบ one-to-many ได้
- Domain specific แบ่งตามการใช้งานเช่น HTTP สำหรับระบบ web application หรือ SMTP สำหรับการส่ง email เป้นต้น
- Messaging สำหรับการส่งข้อมูลในรูปแบบ messaing เช่น queue และ topic เป็นต้น โดยนิยมใช้งาน RabitMQ และ Apache Kafka
- Remote Procedure Invocation (RPI) หรือการเขียนจากระยะไกล เป็นรูปบบที่เก่ามาก ๆ แต่กลับมาได้รับความนิยมอย่างมากในปัจจุบัน ยกตัวอย่างเช่น REST, gRPC และ Apache Thrift เป็นต้น
- API gateway เนื่องจากจำนวน service มีจำนวนมาก ถ้าเรียกใช้งาน service-to-service จำนวนมาก ๆ แล้ว น่าจะทำให้เกิดความยุ่งเหยิงได้ ดังนั้นเราจึงนำ API gateway มาเป็นคนกลางของการติดต่อสื่อสาร แต่ต้องระวังอย่างให้กลายเป็นคอขวดหรือ Single Point of Failure ด้วยละ
ต่อมาคือ เราจะทดสอบกันอย่างไร ? การทดสอบระบบงานเป็นเรื่องที่มีความสำคัญอย่างมาก
เพื่อทำให้เรามั่นใจว่า ระบบงานยังคงทำงานได้ตามที่คาดหวัง ประเด็นที่สำคัญคือ แนวทางในการทดสอบระบบงานของเราเป็นอย่างไร ? ทดสอบแบบ manual หรือว่าแบบอัตโนมัติ ? แน่นอนว่า ในปัจจุบันนั้นการทดสอบแบบอัตโนมัติ น่าจะเริ่มเข้ามาเป็นส่วนหลักไปแล้ว (มั้ง !!) ซึ่งการทดสอบจะเป็นไปตาม Pyramid Testing ดังรูป แต่เมื่อระบบอยู่ในรูปแบบ Microservice แล้ว นั่นคือจำนวน service มากกว่า 1 service คำถามที่น่าสนใจคือ เราจะทำการทดสอบกันอย่างไร ?- Unit testing ทดสอบ code ของแต่ละ service
- Integration testing ทดสอบการทำการกับส่วนอื่น ๆ ที่ใช้งานจาก service เช่น database และ การติดต่อไปยัง service อื่น
- Component testing ทดสอบการทำงานภายในทั้งหมดของ service โดยตัดหรือจำลองการทำงานภายนอก
- Contract testing ทดสอบการทำงานของ service ในมุมมองของผู้ใช้งานเป็นหลัก
- End-to-End testing ทดสอบการทำงานโดยรวมของระบบว่า แต่ละ service ทำงานร่วมกันได้หรือไม่
วันนี้เรายังทดสอบระบบงานแบบ manual กันอยู่หรือไม่ ? ถ้ายังใช่แล้ว คุณคิดว่าคุณพร้อมกับ Microservice หรือไม่ ? แต่ผมมั่นใจว่า ไม่รอดแน่ ๆ
โดยรวมแล้วการพัฒนาแต่ละ service ตามแนวคิดของ Microservice จะประกอบไปด้วยสิ่งต่าง ๆ ดังรูป
ยังไม่พอนะ เราจะทำการ deploy service จำนวนมากกันอย่างไร ?
ก่อนอื่นนั้นต้องดูที่ process การทำงานในปัจจุบันว่าเป็นอย่างไร ? ทั้งการ deploy มีความยุ่งยากและช้าหรือไม่ ? ทั้งการ provisioning พวก infrastructure ทั้งเครื่อง server ทั้งระบบ network รวมถึงระบบรอบข้าง ว่ามีความยุ่งยากและช้าหรือไม่ ? ทั้งการทดสอบว่าเป็นอย่างไร ? แสดงดังรูป ถ้ายังช้าและยุ่งยากแล้ว แนะนำให้แก้ไขก่อนเลย มิเช่นนั้น Microservice จะเข้ามาสร้างปัญหามากกว่าเดิมอย่างแน่นอน เพราะว่า แทนที่จะ deploy เพียง service เดียว ต้องไปดูแล service มากกว่า 1 ตัว !!! ที่สำคัญคือ รูปแบบการ deploy ก็จะเปลี่ยนไปอีกด้วย เช่นอาจจะต้องมีการทำ VM และ container มาช่วยจัดการ รวมไปถึงเครื่องมือใหม่ ๆ ที่ต้องใช้งาน ดังนั้นจำเป็นต้องศึกษาให้มากขึ้น ยกตัวอย่างเช่น Docker, Kubernetes และ Istio เป็นต้น รวมไปถึงเครื่องมือพวก Ansible และ Chef เป็นต้น แสดงดังรูป เราสามารถสรุปรูปแบบของการ deploy Microservice ได้ดังนี้- Multiple services per host
- Single service per host
- Service per VM
- Service per container
คำถามก่อนที่จะเริ่มนำ Microservice มาใช้งานคือ เราพร้อมที่จะเริ่มต้นทำความรู้จักกับ Microservice แล้วหรือยัง ?
เพื่อทำให้เห็นข้อดีและข้อเสียของมัน เพื่อหาวิธีการแก้ไขและรับมือ ก่อนที่จะลงมือทำหรือใช้งานต่อไป อีกอย่างที่เราต้องเข้าใจก่อนคือ เราพร้อมหรือไม่ ? ทั้ง skill ของทีมพัฒนา และคนในองค์กร ทั้ง process ของการพัฒนา ทั้งระบบ Continuous Integration และ Continuous Delivery รวมถึง DevOps ก่อนที่จะไปถึง Microservice แสดงดังรูปสุดท้ายแล้วนั้น ปัญหาของคุณคืออะไร ถึงจะนำแนวคิด Microservice มาใช้งาน ?Reference Websites https://martinfowler.com/articles/microservices.html https://www.nginx.com/blog/building-microservices-using-an-api-gateway/ https://martinfowler.com/articles/microservice-testing/ https://www.xenonstack.com/blog/devops/microservices-testing-strategy-automation-architecture/ https://www.gocd.org/2018/05/08/continuous-delivery-microservices-test-strategy.html
Observer pattern ต่างกับ Publish-Subscribe pattern อย่างไร ?
Observer pattern
The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.นั่นหมายความว่า ใน Observer pattern จะมีการทำงาน 2 ส่วนคือ subject กับ observer โดยที่ subject จะมีรายชื่อของ depedency หรือ observer ไว้ จากนั้นเมื่อมีการเปลี่ยนแปลงที่ subject จะแจ้งไปยัง observer ทั้งหมดที่ดูแลหรือผูกไว้ จะใช้ในระบบที่จัดการกับ event หรือเหตุการณ์ต่าง ๆ แบบ synchronous ยกตัวอย่างเช่น เมื่อเกิดการเปลี่ยนแปลงหรือเหตุการณ์ที่หน้าจอผู้ใช้งานแล้ว จะทำการแจ้งไปยัง observer ที่ผูกไว้ทั้งหมดทันที (ด้วยการเรียก method เช่น notify() หรือ notifyAll() เป็นต้น) เป็น pattern ที่ถูกใช้บ่อยอย่างมาก แต่เนื่องจากทาง Subject ต้องผูกมัดกับ Observer ไว้ก่อน ดังนั้นอาจจะทำให้เกิด memory leak ได้ เพราะว่าถ้าผูกมัดแล้วไม่มีการคืนหรือแก้การผูกมัดนั่นเอง บางทีเราเรียกว่า Storng reference นั่นคือสิ่งที่เราต้องแก้ไขให้เป็น weak reference
Publish-subscribe pattern
publish–subscribe is a messaging pattern where senders is messages, called publishers, do not program the messages to be sent directly to specific receivers, called subscribers, but instead categorize published messages into classes without knowledge of which subscribersนั่นหมายความว่า ใน Publish-subscribe pattern นั้นจะมีการทำงาน 3 ส่วนคือ Publisher, Subscriber และคนกลาง โดยที่ Publisher คือผู้ส่ง message ต่าง ๆ คำถามคือส่งไปที่ไหน ? ตอบง่าย ๆ ไปที่คนกลางไงล่ะ !! คนกลางคือ Subscriber ใช่ไหม ? ตอบว่า ไม่ใช่ แต่เป็นส่วนที่ไว้รับ message ต่าง ๆ เช่น Broker, Message broker หรือพวก Event bus , Queue messaging หรือ Middleware จะเยอะไปไหน งงไปหมด !! มาถึงส่วน Subscriber กันบ้าง แน่นอนว่า Publisher นั้นจะไม่รู้จักกับ Subscriber เลย คำถามคือ แล้ว Subscriber จะไปเอา message จากไหนมาทำงาน ? คำตอบคือ ก็ไปขอข้อมูลจากคนกลางไงละ นั่นหมายความว่า คนกลางจะมีช่องทางให้ไปลงทะเบียนว่า Subscriber แต่ละตัวอยากจะทำงานกับอะไร ก็ไปลงทะเบียนไว้ เมื่อมี message หรือ event เข้ามาก็นำไปทำงานได้เลย ดังนั้นทั้ง Publisher และ Subscriber จึงไม่ต้องรู้จักกันเลย หรือพูดได้ว่าไม่ได้ผูกมัดกันเลยหรือแบบหลวม ๆ (Loose coupling) ส่วนคนกลางก็มีบทบาทสำคัญมาก ๆ ทั้งรับ message/event เข้ามาจากทาง Publisher และคอยให้ทาง Subscriber มาลงทะเบียน ซึ่ง pattern นี้ถูกใช้งานในระบบแบบกระจายอย่างมาก ที่สำคัญยังสามารถ scale ได้ง่ายอีกด้วย เช่นการเพิ่ม Subsciber เข้าไป แต่สิ่งที่ต้องพึงระวังให้มาก ๆ คือ คนกลางนั่นเอง ถ้าล่มหรือทำงานไม่เสถียรจะส่งผลร้ายแรงต่อระบบงานมาก ๆ
มาถึงตรงนี้น่าจะพอทำให้เห็นว่า ทั้งสอง pattern ต่างกันอย่างไรแล้วหรือไม่ ?
Reference Websites https://hackernoon.com/observer-vs-pub-sub-pattern-50d3b27f838c https://medium.com/@huytrongnguyen1985/from-pub-sub-pattern-to-observer-pattern-f4ae1e425cc9 https://www.quora.com/What-is-the-difference-between-observer-and-pub-sub-patternเรื่องของ API Gateway จากหนังสือ Practical Site Reliability Engineering (SRE)
- เป้าหมายของ SRE เป็นอย่างไร
- Docker นั้นช่วยเปิดทางให้เข้าสู่โลกของ container ได้อย่างไร
- ว่าด้วยเรื่องของการนำ DevOps และ Microservice มาให้ให้เกิดประโยชน์
- แนวคิดของ Service Mesh ตลอดจนการนำมาใช้งาน
- แนวปฏิบัติที่ดีสำหรับเรื่องของ performance และ reliability ของระบบ
- ว่าด้วยเรื่องของการจัดการ container จำนวนมาก ๆ ด้วย Kubernetes
- เข้าใจขั้นตอนการพัฒนาระบบงานตั้งแต่ต้นจนจบ ด้วยการนำ container มาใช้งาน
API Gateway คืออะไร ?
เรียนได้ว่าเป็น Single Point of Contract ของ service จำนวนมากมายของเรา ยิ่งในโลกของ Microservice จะมี service จำนวนมาก ทำให้การเข้าถึง service เหล่านั้นน่าจะยาก จึงน่าจะมีตัวกลางในการเข้าถึงดีกว่าไหม ? โดยใน API Gateway นั้นจะมีความสามารถต่าง ๆ ดังนี้- ทำการ register และ publish service ต่าง ๆ
- เป็นคนกลางในการจัดการ service ต่าง ๆ
- เป็นตัว route ไปยัง service ต่าง ๆ
- ทำการ verification ของ request ต่าง ๆ
- ทำการกรองข้อมูล ก่อนที่จะส่งต่อไป
- ทำการ authentication และ authorization แต่ละ request
- ทำการ monitoring การทำงานของ service ต่าง ๆ
ประโยชน์ของ API Gateway มีอะไรบ้าง ?
- ซ่อนการเปลี่ยนแปลงของ service จากผู้ใช้งานภายนอก
- จัดการเรื่องของ security ของ service ได้ง่ายขึ้น
- เป็นตัวแปลง protocol การติดต่อสื่อสารระหว่าง service กับผู้ใช้งาน เช่นผู้ใช้งานติดต่อมายัง API Gateway ผ่าน HTTP จากนั้น API Gateway ก็ทำการติดต่อไปยัง service ผ่าน protocol อื่น ๆ ตามแต่ละ service ต่อไปได้
- ลดความซับซ้อนของ Microservice ลง ทำให้แต่ละ service ไปเน้นที่ business เป็นหลัก
เมื่อมันมีข้อดีก็ต้องมีข้อเสีย !!!
- กลายเป็น Single Point of Failure ไปได้
- ใครเป็นคนดูแลระบบนี้ ?
- จำเป็นต้องมี infrastructure ที่ดี
- อาจจะทำให้ business logic ไปอยู่ใน API Gateway มากเกินไป ทำให้เกิด vendor locked-in ได้ง่าย
- เกิดความซับซ้อนขึ้นมาในอีกจุด และอาจจะทำให้เกิดปัญหาคอขวดได้
อีกเรื่องที่น่าสนใจคือ Security ใน API Gateway
ถ้าเราเพิ่งพามาก ๆ จำเป็นต้องมีรูปแบบที่ดี เพื่อทำให้มั่นใจว่า service ของเราปลอดภัยทั้งการทำงานและข้อมูล ยกตัวอย่างดังรูปแสดงเรื่องของ authentication และ authorizationคำถามที่น่าสนใจคือ ทำไมระบบเราต้องมี API Gateway ด้วยละ ?
ว่าด้วยเรื่องของ Project ที่ล้มเหลว
ปล. ปกติเราน่าจะทำ product มากกว่า project กันอยู่แล้ว ดังนั้นไม่น่าจะ fail กันมากหรอก ใช่ไหมนะเคยไหม ? เช้าวันหนึ่งของวันพุธเวลา 10.00 น. เข้าประชุมเพื่อสรุปงาน ได้ข้อสรุปว่า เราต้องทำงานนี้ให้เสร็จภายในวันพุธนะ คำถามที่ลอยมาคือ วันนี้วันพุธนะ คำตอบที่ลอยมาคือ ใช่แล้ว เริ่มทำกันเลย สู้ ๆ ผลที่ออกมาจะเป็นอย่างไรบ้าง ? มันก็ขึ้นอยู่กับสถานการณ์และสิ่งแวดล้อมที่แตกต่างกันไป มีทั้งเรื่องดีและไม่ดี หรืออย่าไปพูดถึงมันเลย ให้มันผ่าน ๆ ไปเถอะ หนึ่งสิ่งที่อาจจะเกิดขึ้นได้คือ งานเหล่านั้นล้มเหลวไม่เป็นท่า ดังนั้นเรามาดูกันหน่อยว่า สาเหตุของความล้มเหลวในการพัฒนาระบบงานเกิดจากอะไรบ้าง ?
สาเหตุแรกคือ ขอบเขตของงานไม่ชัดเจน หนักกว่านั้นคือไม่มีเลย
หนักกว่านั้นคือ เอาเหมือนระบบนั้น หรือ เอาเหมือนเดิม น่าจะเป็นปัญหาหลัก ๆ ของความล้มเหลวเลย คนบอกความต้องการก็ไม่ชัดเจน คนทำก็ยิ่งไม่รู้เรื่อง สิ่งที่ทำ ๆ ไป ก็ได้เพียงแค่สร้างมันขึ้นมา อาจจะจบด้วยว่า เราได้เรียนรู้อะไรบ้าง แต่ผลตรง ๆ คือ ล้มเหลว หรือสิ่งที่ทำออกมาไม่ได้ใช้หรือ โยนทิ้ง คนทำจะรู้สึกกันอย่างไรบ้างหรือทำไปสักครึ่งทาง แล้วมาบอกว่าเปลี่ยนใหม่เถอะ !! เปลี่ยนอีกแล้วหรอ ? เพิ่งเปลี่ยนไปเมื่อเช้าเองนะ !!
สาเหตุที่สองคือ แก้ไขปัญหาผิดเรื่อง
ถ้างานที่ทำนั้นชัดเจนมาก ๆ แต่สิ่งที่กำลังทำนั้น ดันไปแก้ไขปัญหาผิดเรื่อง นั่นคือ คนใช้งานน้อยหรือไม่มีคนใช้งานนั่นเอง คำถามคือ งานที่ทำน่าจะล้มเหลวใช่ไหม ?สาเหตุที่สามคือ มีการสื่อสารที่แย่
ยิ่งองค์กรที่มีความซับซ้อนมาก ๆ ยิ่งต้องมีการพูดคุยกับหลายฝ่าย ทั้งภายในและภายนอก ส่งผลให้เกิดข้อผิดพลาดหรือเข้าใจผิดมากมายสาเหตุที่สี่คือ แต่ละคนไม่สนใจงานอื่น ๆ นอกจากงานของตัวเอง
เมื่อเกิดปัญหาขึ้นมา แต่ละคนจะโทษกันไปกันมา เช่น Backend team จะไปโทษ frontend team Frontend team จะไปโทษคนออกแบบ คนออกแบบจะไปโทษคนให้ requirement คนให้ requirement จะไปโทษ business Business จะไปโทษ .... คำถามคือ ใครผิด ? เมื่อเป็นเช่นนี้ก็ทำให้ไม่มีใครอยากจำทำอะไร เสียทั้งเวลาและความรู้สึกสาเหตุที่ห้าคือ เอกสารแย่มาก ๆ
เอกสารที่มีไม่สามารถ tracking อะไรได้เลย ว่า requirement เป็นอย่างไร ตรวจรับอย่างไร ออกแบบอย่างไร พัฒนาอย่างไร ทดสอบอย่างไร Deploy อย่างไร เมื่อเกิดปัญหาขึ้นมาแล้ว กระทบอะไรส่วนไหนบ้าง หนักกว่านั้น ไม่ทำการ update ให้เป็นล่าสุดเลย แต่ต้องทำ เพราะอะไรกันนะ ถ้าให้เลือกระหว่าง code กับเอกสาร คุณจะเชื่อหรือเลือกอะไร ?สาเหตุที่หกคือ เตรียมการแย่มาก ๆ
ทั้งเรื่องของ requirement ทั้งเรื่องของ system ทั้งเรื่องของการวางแผน ทั้งเรื่องของ infrastructure ทั้งเรื่องของ environment หนักกว่านั้นคือ เรื่องของทีม ที่ไม่มี skill ตามที่ต้องการ โยกคนไปมาตามใจต้องการ ใครว่างก็เอาเข้ามาทำไป เน้นแต่จำนวน แต่ไร้คุณภาพเอาเท่านี้ดีกว่า ยังมีเรื่องอะไรอีกนะ !!
แนวโน้มของ DevOps ที่กำลังมาในปี 2019
DevSecOps
นำเรื่องของ security เข้ามาในขั้นตอนการทำงานตามแนวคิด DevOps ด้วย ดังนั้นแทนที่จะทำการตรวจสอบเรื่องของ security ในช่วงท้ายของการพัฒนาระบบงาน ก็ให้นำเรื่องของ security หรือ non-functional requirement ที่ชอบพูดถึงกัน มาพูดคุยกันต้องแต่การคุย requirement, analysis, design, coding, และ deploy กันไปเลย นั่นหมายความว่า เราต้องนำเรื่องของ security เข้ามาคุยวางแผน ลงมือทำในทุก ๆ ขั้นตอน เพื่อทำให้เรารู้ปัญหาได้รวดเร็ว เพื่อที่จะแก้ไขปัญหาได้ทันท่วงทีนั่นเอง ทำให้ส่งผลต่อ เรื่องของแนวคิดของคนทำงาน เรื่องของ skill ของคนทำงาน เรื่องของ process การทำงาน อย่างแน่นอน ที่สำคัญขั้นตอนและเครื่องมือเรื่อง security น่าจะเริ่มมาทำงานแบบอัตโนมัติมากยิ่งขึ้น ผลที่ตามมาคือ สามารถส่งระบบงานที่ดีและมีคุณภาพให้ลูกค้าหรือผู้ใช้งานได้บ่อยขึ้นAI/Data with DevOps
เรื่องของ DevOps จำเป็นต้องนำข้อมูลต่าง ๆ มาใช้งาน เพื่อทำการวิเคราะห์ ปัญหาหรือแนวโน้มต่าง ๆ ที่จะเกิดขึ้น ดังนั้นเรื่องของ Data-Driven และ AI จึงจะเข้ามามีบทบาทอย่างมาก ปัญหาแรก ๆ คือ การเก็บข้อมูล เราต้องเลือกว่าจะเก็บข้อมูลอะไรบ้าง ไม่ใช่เก็บทั้งหมด ข้อมูลที่จัดเก็บอยู่ในรูปแบบใด ที่จะทำให้นำไปใช้งานได้ง่าย การจัดเก็บก็สำคัญ เนื่องจากข้อมูลมีปริมาณมาก และเกิดขึ้นรวดเร็วมาก ๆ การประมวลผลข้อมูลก็สำคัญว่าจะเป็นแบบ realtime หรือ batching รวมทั้งการนำ AI และ Data Science เข้ามาช่วยเหลือ ก็น่าจะทำให้เห็นหรือเข้าใจข้อมูลเชิงลึกได้มากยิ่งขึ้น รวมทั้งเข้าใจพฤติกรรม และ แก้ไขปัญหาได้รวดเร็ว หรือรู้ปัญหาก่อนที่จะเกิดขึ้นได้อีกด้วย อีกทั้งสามารถส่งมอบ feature ใหม่ ๆ ที่ตอบโจทย์ผู้ใช้งานมากขึ้นอีกด้วยWorking with Containeraization และ Microservice
คำว่า Agile หรือ Agility หรือความคล่องตัว รวดเร็วมาแล้ว คำว่า Microservice มาแล้ว คำว่า Container สำหรับการจัดการ Microservice มาแล้ว ยิ่งทำให้ DevOps ยิ่งสำคัญว่า จะส่งมอบระบบงานที่มีคุณภาพและรวดเร็วได้อย่างไร ตัวอย่างเช่น การจัดการ container จำนวนมาก ๆ จะจัดการอย่างไร ? เช่นต้องมีเครื่องมือพวก Docker และ Kubernetes มาช่วยจัดการหรือไม่ ? ต้องเริ่มนำ Serverless หรือ Function-as-a-Service เข้ามาใช้งานหรือไม่ ?Focus on Continuous Delivery (CD)
ยิ่งระบบมีความซับซ้อนสูงขึ้น การทำงานแบบ manual นั้นเป็นสิ่งที่ยากลำบากมากขึ้น ดังนั้นเรื่องของ Continuous Delivery จึงมีความสำคัญมาก ๆ ทั้งเรื่องของ Continuous Integration Continuous Testing Continuous Deployment/Delivery Continuous Provisioning Continuous Monitoring ทั้งหมดนี้พยายามนำมาอยู่ใน build/deployment pipeline ของ Continuous Delivery นั่นเอง เพื่อร้อยเรียงสิ่งต่าง ๆ ข้างต้นเข้าด้วยกัน เพื่อทำงานแบบอัตโนมัติให้ได้มากที่สุด จากนั้นคนเราก็จะได้ไปสนใจเรื่องของการพัฒนาเพื่อตอบโจทย์ business และลูกค้ามากยิ่งขึ้นNoOps
สุดท้ายคือการย้ายจาก DevOps ไปสูง NoOps (No Uncomfortable Operation) นั่นหมายความว่า ถ้าใครอยากได้อะไรก็ตาม ก็ทำการร้องขอผ่านระบบ self-service จากนั้นเมื่อทำการ approve เรียบร้อย ก็จะได้ระบบหรือสิ่งที่ต้องการเลย ยกตัวอย่างเรื่องของ environment ต่าง ๆ เป็นต้น ไม่ต้องมาเสียเวลารอกันไปมาอีกแล้ว ระบบนี้จะเหมือนกับ Platform-as-a-Service (PaaS) นั่นเอง แต่จำเป็นต้องการทั้งคนที่มี skill และเครื่องมือเพื่อสร้างระบบต่าง ๆ เหล่านี้ขึ้นมาทั้งหมดนี้มีเป้าหมายเพื่อ ปรับปรุงการพูดคุยติดต่อสื่อสารและการทำงานร่วมกันให้ดีขึ้น แน่นอนว่าต้องการวินัยในการลงมือทำ และเครื่องมือเพื่อรองรับความต้องการ แต่ถ้าทำแล้วแย่ลงกว่าเดิม ไม่น่าจะถูกนะ !!
ภาษาโปรแกรมที่น่าจับตามองในปี 2019 จาก O’Reilly
- Python
- Java
- Go
- C#
- Kotlin
- Rust
คำแนะนำสำหรับการเขียน code ด้วยภาษา Java
- ไม่รู้จัก JAVA_HOME
- ไม่รู้จัก CLASSPATH
- ไม่สามารถ compile และ run ผ่าน command-line ได้
- ไม่ได้ใช้ความสามารถของ IDE ได้อย่างเต็มที่และถูกต้อง
สุดท้ายแล้ว ถ้าคุณเป็น Java Developer แล้ว คงไม่พลาดกับ Kotlin นะขอให้สนุกกับการ coding ครับ อีกอย่างคือ เราฝึกเขียน code กันตอนไหนบ้าง ?
สรุปจากบทความเรื่อง Don’t be the software developer you hate to work with
ข้อที่ 1 เขียน code ที่ยุ่งเหยิง เข้าไจยาก ดูแลยาก
เป็นข้อสำหรับนักพัฒนา software มาก ๆ ทั้งการตั้งชื่อ ทั้งการไม่ update หรือ แก้ไข comment เมื่อเปลี่ยน code ทั้งไม่ใช้ code formatter แม้จะใช้ IDE ก็ตาม ทั้งไม่สนใจ warnning ต่าง ๆ จาก lint และ IDE การกระทำเหล่านี้ มันส่งผลและรบกวนจิตใจต่อคนอื่น ส่งผลให้การพัฒนาช้าลงไป ยกตัวอย่างเช่น ในการ review code ต้องมาเสียเวลาจัด format ของ code และแก้ไข warning ต่าง ๆ ซึ่งมันไม่น่าจะใช่เรื่องที่ต้องมาทำในการ review code นะ หนักกว่านั้นเมื่อนักพัฒนาทุกคนรู้สึกปลง หมดอาลัยตายอยากกับสิ่งที่ทำไปอีก นั่นคือ ใคร ๆ ก็ไม่ทำ ดังนั้นเราจะทำไปทำไม สิ่งที่เกิดขึ้นคือ ก็ทำไปวัน ๆ แก้ไขไปวัน ๆ ส่งผลให้เกิดข้อผิดพลาดจำนวนมาก บ่อยครั้งมักจะผิดซ้ำเรื่องเดิม ๆ อีกด้วย ซึ่งมันเสียทั้งเงินและเวลาไปมากมาย นักพัฒนา software ต้องมีความเป็น Software CraftsmanshipCraftsmanship is a way of thinking and doing where humanity is in tune with nature, not working against it.มีความเป็นมืออาชีพในสิ่งที่ทำ ต้องทำให้มันดีขึ้นอย่างต่อเนื่อง แน่นอนว่า ต้องมีการฝึกฝนอยากหนักและถูกต้อง รวมทั้งเปิดรับข้อแนะนำต่าง ๆ เพื่อโอกาสดี ๆ ที่จะตามมา ตั้งแต่ข้อที่ 2 ไปนั้น สามารถใช้ได้กับสายอาชีพอื่น ๆ แน่นอน
ข้อที่ 2 ไม่เคารพผู้อื่น เช่นการมาไม่ตรงเวลา
ยกตัวอย่างเช่น เข้าทำงานหรือประชุมสายเป็นประจำ แต่มันก็ขึ้นอยู่กับการประชุมนั้นต้องการเราหรือไม่ ถ้าต้องการก็อาจจะรอเรา นั่นคือเสียเวลาไปอย่างมาก ไม่ต้องการเรา แต่ก็ต้องมาอธิบายหลังจากที่ประชุมเสร็จ แบบนี้ก็เสียเวลาเพื่อเข้ามาอีก ดังนั้นมาให้ตรงเวลาหรือก่อนเวลา ข้อนี้ผมเป็นบ่อยเลย ต้องปรับปรุงให้มาก ๆข้อที่ 3 ชอบปัดความรับผิดชอบ ที่ไม่เกี่ยวกับ code ออกไป
ยกตัวอย่างเช่น เราทำ product A แต่ทำในเฉพาะส่วน backend แต่เมื่อได้รับข้อติชมมาว่า ในส่วนของ UI ไม่ค่อยดีนะ ใช้งานยาก หรือแสดงผลผิดพลาดแล้ว เรามักจะบอกว่า ผมทำในส่วน backend นะ ไม่ได้ทำในส่วนของ UI หรืออาจจะบอกว่า ไม่ได้เขียนนะ หรืออาจจะบอกว่า มันแย่เพราะว่า ไม่มี skill หรือ ประสบการณ์นะ หรือมีบางอย่างผิดขึ้นมา เรามักจะบอกว่า เราไม่ได้ทำก่อนเลย รูปแบบนี้ ไม่น่าจะใช่สิ่งที่ดีสำหรับการทำงาน ดังนั้นควรมีทั้ง responsibility และ accountability ในการทำงานด้วย ควรมองที่ภาพรวมหรือภาพใหญ่ของระบบ ไม่ใช่ดูเพียงในส่วนงานที่เราทำเท่านั้น ถึงเราจะไม่ใช่ผู้เชี่ยวชาญ แต่สามารถเรียนรู้และฝึกฝนกันได้ เพื่อทำให้งานมันออกมาดีกว่าเดิมข้อที่ 4 เมื่อเกิดปัญหาขึ้นมา เรามักจะหาเหตุผลมาแก้ตัว มากกว่าหาวิธีการแก้ไข
ยกตัวอย่างเช่น ทำไม product A มีปัญหาหรือข้อผิดพลาดเยอะ ? นักพัฒนามักจะบอกว่า เวลามีน้อย Requirement ไม่ชัด เปลี่ยนไปเปลี่ยนมา ความรู้ไม่เพียงพอ เราไม่เคยใช้เทคโนโลยีนี้ งานมันยากและซับซ้อน พอฟัง ๆ แล้วทำมันมันมีแต่การหาเหตุผลมาแก้ตัว ฟังแล้วดูเหมือนจะไม่ผิดเลย และฟังดูหดหู่มาก ๆ ดังนั้นเมื่อเกิดปัญหาขึ้นมา น่าจะมาพูดว่า เรามีวิธีการอะไรบ้าง ที่จะช่วยแก้ไขปัญหาเหล่านี้ให้น้อยลงหรือหมดไปได้ ซึ่งน่าจะช่วยทำให้ทีมสามารถเรียนรู้จากปัญหา และเราจะไม่ผิดซ้ำที่เดิม รวมทั้งช่วยให้ทีมช่วยเหลือและออกความคิดเห็นกันอีกด้วยข้อที่ 5 พูดไปเรื่อย ไม่เข้าประเด็น หรือเข้าปัญหาและการแก้ไขสักที
บ่อยมากที่เราจะเจอคนที่พูดไปเรื่อย วนไปเรื่อย อ้อมไปเรื่อย แล้วจะจบที่ว่า เราไม่ได้อะไรเลย สรุปอะไรไม่ได้เลย บางทีดูดี แต่จับต้องอะไรไม่ได้เลย เสียเวลามาก ๆ ดังนั้นเราควรพูดให้ตรงประเด็น ตรงปัญหา ตรงเรื่องที่ต้องการจะพูดคุย ไม่ต้องมาอ้อมค้อมให้เสียเวลาข้อที่ 6 บ่นไปทุกเรื่อง คิดในแง่ลบ แถมไม่ลงมือทำอีกด้วย
มักจะเจอคนลักษณะนี้เยอะ คือบ่นไปได้ทุกเรื่อง คิดแต่แง่ลบ แต่พอให้ลงมือทำกลับเป็นง่อย ทำไม่ได้ ไม่ทำ แต่พอตอนคิด พูด present นี่เก่งจัง !! ผมน่าจะเป็นด้วยนะ 555ข้อที่ 7 ในที่ประชุมชอบพูดเรื่อยเปื่อย ไม่จบหรือเข้าประเด็นสักที
บ่อยครั้งที่คนในห้องประชุม ต้องนั่งมองเพดาน หรือหมดอารมณ์จะพูดคุยต่อไป ถ้าไม่กลุ่มคนเหล่านี้ แนะนำให้เชิญออกไปจากห้องซะข้อที่ 8 ข้อนี้น่ากลัวมาก ๆ คือ ความดีเอาใส่ตัว ความชั่วให้คนอื่น
พูดง่าย ๆ คือ ชอบขโมย credit จากของคนอื่นมาใส่ตัวเอง เช่นแทนที่จะบอกว่าสิ่งต่าง ๆ เหล่านี้มาจากทีม ก็บอกไปว่า นี่คือสิ่งที่ผมลงแรงทำมากับมือเดียว !! เมื่อเราเจอคนแบบนี้บ่อย ๆ ก็จะเอือมระอา และไม่อยากทำงานด้วย ดังนั้นเราควรให้ credit ซึ่งกันและกันอยู่อย่างเสมอ แต่ไม่ใช่ทำผิดก็ยังบอกว่า คุณทำดีแล้วหรือยังชมกันอีกละ แบบนี้มันก็เกินไป ผิดก็บอก ถูกก็ชื่นชม มันสำคัญมาก ๆ สำหรับการทำงานเป็นทีมสุดท้ายแล้ว เราเป็นนักพัฒนา software ที่กระทำสิ่งต่าง ๆ ทั้ง 8 ข้อมาบ้างหรือไม่นะ ?
แอบไปดู OWASP Docker Top 10 กันหน่อย
สิ่งที่ต้องเข้าใจก่อนก็คือ
Docker นั้นไม่ได้แก้ไขปัญหาเรื่องของ security ไม่ได้ช่วยเรื่องของ input validation ไม่ได้ช่วยป้องกันเรื่องของ SQL Injection เรื่องเหล่านี้มันคือ Application Security ล้วน ๆ การนำ Container มาใช้งานนั้น กระทบหลายส่วนมาก ๆ ทั้งการ deployment ทั้งเรื่องของ system ทั้งเรื่องของ network ทั้งเรื่องของ hardware แน่นอนว่า เรื่อง security ก็จำเป็นอย่างมาก ซึ่งเป็นที่มาของ project นี้ ก่อนที่จะเริ่มต้น environment ของเราต้องปลอดภัยก่อน นั่นคือการวางแผน จากนั้นก็จะมี cost ในการ rollout ตามมาอีก !!เรามาดูกันดีกว่าว่า Top 10 มีอะไรกันบ้าง แบบสั้น ๆ ไม่รู้ว่าเข้าใจถูกไหม !!
- D1 Secure User Mapping คือ application ใน container ไม่ควร run ด้วย root
- D2 Patch Management Strategy คือไม่ว่าจะเป็นเครื่อง host, base image และเครื่องมือในการจัดการ container อะไรมักจะมี security bug เสมอ ดังนั้นจำเป็นต้องมีการจัดการเรื่องของการทำ patching แบบปกติและฉุกเฉินไว้ด้วย
- D3 Network Separation and Firewalling คือ การออกแบบ network ของระบบสำคัญมาก ๆ ทั้ง network ของเครื่องมือในการจัดการ ทั้ง network ของ container ต่าง ๆ ว่า container ใดจะเข้าถึงได้จากภายในเท่านั้นหรือภายนอกเท่านั้น
- D4 Secure Defaults and Hardening คือการทำให้แน่ใจว่ามีเพียงสิ่งที่เราต้องการใช้งานเท่านั้นที่ถูกติดตั้งและทำงานอยู่ ไม่ว่าจะเป็นที่ host, container และเครื่องมือในการจัดการ
- D5 Maintain Security Contexts คือการแยกการทำงานของแต่ละส่วนงานให้อยู่ต่างเครื่องกัน เช่น frontend กับ backend เป็นต้น เพื่อให้สามารถจัดการความปลอดภัยแยกกัน ซึ่งช่วยลดความผิดพลาดได้ดีขึ้น
- D6 Protect Secrets คือการจัดการพวก secret ต่าง ๆ ให้ดีทั้ง password, token และ private key เป็นต้น
- D7 Resource Protection คือการจัดการใช้งาน resource ของแต่ละ container ให้อยู่ในขอบเขตที่กำหนดเสมอ (limit resources) ทั้ง CPU, Memeory, Disk และ Network เพื่อไม่ให้ไปกระทบต่อการทำงานของ container อื่น ๆ
- D8 Container Image Integrity and Origin คือการจัดการ Image ที่ปลอดภัย ว่าจะไม่ถูกเปลี่ยนแปลงตั้งแต่ต้นจนถึงการ deploy
- D9 Follow Immutable Paradigm คือ container ส่วนใหญ่ไม่ต้องการเขียนข้อมูลหรือ mount ไปยัง file system ดังนั้นกำหนดให้ container ทำงานแบบ read-only ก็พอ ช่วยทำให้มีความปลอดภัยมากขึ้น
- D10 Logging คือทุก ๆ container, host และเครื่องมือในการ deploy ไม่ว่าจะเกิดเหตุการณ์ใด ๆ ก็ตามจะต้องถูกบันทึกไว้เสมอ เพื่อให้สามารถพิสูจน์การทำงานและตัวตนได้ โดย log ไม่ได้เขียนไว้ใน container แนะนำให้เก็บแบบ remote หรือพวก centralize log ไป
- Developer
- Auditor
- Architect
- System และ Network engineer
ลองศึกษาเพิ่มเติมดูได้ครับ น่าสนใจมาก ๆ
สรุปบทความเรื่อง Write tests. Not too many. Mostly integration
Write tests. Not too many. Mostly integration. https://twitter.com/rauchg/status/807626710350839808จาก tweet นี้สามารถลงรายละเอียดไปได้ 3 ส่วนคือ
ส่วนที่ 1 คือ Write tests
ระบบงานส่วนใหญ่ควรจะเขียน automated test ถ้าคุณเห็นว่าเวลาที่เสียไปมีคุณค่า ช่วยให้เราหาข้อผิดพลาดใน environment ที่เราควบคุมได้ เพื่อที่จะได้แก้ไข ซึ่งมันดีกว่าไปเจอบน production แน่นอน ส่งผลให้ลดเวลาการทำงานลงไปเยอะ เมื่อเราเขียน automated test ในการสร้าง automated test นั้นอาจจะใช้เวลานานหรือไม่นานก็แล้วแต่ แต่สิ่งหนึ่งที่ได้กลับมาคือ ลดเวลาในการดูแลรักษาระบบนั่นเองสิ่งที่ควรคำนึงในการเขียน automated test คือ คุณมีความมั่นใจมากน้อยเพียงใด ที่มันจะช่วยทำให้ระบบงานเราไม่มีข้อผิดพลาดเช่นการใช้งาน static typing และ lint น่าจะช่วยให้เรามีความมั่นใจมากยิ่งขึ้น ว่า code ที่เราเขียนขึ้นมาถูกต้องตามมาตรฐาน แต่ไม่ได้บอกว่า business logic จะทำงานได้อย่างถูกต้อง ไร้ข้อผิดพลาด ดังนั้นเราต้องเพิ่มความเชื่อมั่นด้วยการเขียน automated test ที่ดี ๆ เข้ามาเพิ่มนั่นเอง
ส่วนที่ 2 คือ Not too many
ไม่เขียน automated test มากจนเกินไป หลาย ๆ องค์กรเมื่อได้ยินเรื่องของ code coverage แล้ว ทาง manager และ team มักจะชอบตั้งเป้าหมายให้ค่า coverage สูง ๆ เช่น 100% ดังนั้นเมื่อค่า code coverage ลดลง สิ่งที่นักพัฒนาต้องทำคือ เขียน automated test เพิ่มเติมเพื่อให้ code coverage เพิ่มขึ้น สิ่งเหล่านี้คือ ปัญหา เนื่องจากเราจะเสียเวลามากมายไปกับการเขียน automated test ส่วนใหญ่จะไม่จำเป็นต้องทดสอบ ที่สำคัญการดูแล automated test แบบนี้จะทำให้เราและทีมช้าลงอย่างมากรวมทั้งถ้าเราทดสอบในรายละเอียดมากจนเกินไป สิ่งที่ตามมาคือ เมื่อมีการเปลี่ยนแปลง code แล้ว จะกระทบต่อ automated test จำนวนมาก ซึ่งเป็นสิ่งที่ไม่ดีเลยแต่ถ้าสำหรับ open source project มักจะมีค่า code coverage 100% เพราะว่า project มีขนาดเล็ก และเป็นเครื่องมือที่ถูกนำไปใช้ในหลายกรณี ดังนั้นเรื่อง breaking change จึงเป็นสิ่งที่สำคัญมาก ๆ ต้องระมัดระวังด้วยการเขียน automated test ที่ครอบคลุม
ส่วนที่ 3 คือ Mostly integration
การทดสอบนั้นมีหลายชนิดมาก ๆ แล้วแต่แหล่งที่มากันเลย ยกตัวอย่าเช่น Unit, Integration และ End-to-End testing โดยแต่ละชนิดก็มีทั้งข้อดีและข้อเสีย ในบทความจะยกตัวอย่างจาก slide เรื่อง Testing JavaScript Application จากรูปนั้นคือ Test Pyramid โดยเวลาและ resource ที่ต้องใช้สำหรับการเขียน ทดสอบ และดูแลรักษา automated test จะมากขึ้นจากล่างขึ้นบนคือ Unit > Integration > End-to-End นั่นหมายความว่า ให้เน้นในส่วนของ Unit test มาก ๆ แต่สิ่งหนึ่งที่ไม่ได้แสดงใน Test Pyramid คือ ความเชื่อมั่น ซึ่ง End-to-End test นั้นสร้างและทดสอบก็ช้า ใช้ resource เยอะ แต่เรื่องของความเชื่อมันในการทดสอบก็สูงด้วย ดังนั้นผู้เขียนบทความจึงเน้นไปที่เรื่องของความเชื่อมั่นของการทดสอบ จึงได้คิด The Test Trophy ขึ้นมา https://twitter.com/kentcdodds/status/960723172591992832?lang=en แน่นอนว่า แนวคิดนี้ก็มีข้อขัดแย้งกับ Unit test แสดงดังรูป โดย Unit test ก็คือการทดสอบในแต่ละส่วน ซึ่งทำงานถูกตามที่ต้องการ แต่ไม่ได้หมายความว่า รวมกันแล้วจะทำงานได้ถูก ดังนั้นสิ่งที้่ขาดไม่ได้คือ แต่ละส่วนงานต้องทำงานด้วยกันได้ นั่นคือ Integration test นั่นเอง สิ่งที่ต้องคิดเพิ่มคือ ในการเขียน automated test นั้น ต้องคิดถึงทั้งความเชื่อมั่น ความเร็วและค่าใช้จ่ายที่ลงไป ว่ามันคุ้มไหม ?How to write more integration tests ?
เส้นแบ่งระหว่าง unit และ integration test นั้นอาจจะทำให้สับสนได้ แต่สิ่งที่สำคัญมาก ๆ สำหรับการเขียน integration test คือ หยุดการใช้ mock หรือลดการใช้ mock ให้น้อยที่สุด ใช้เท่าที่จำเป็น เพราะว่า การใช้ mock คือการลดความเชื่อมั่นของ integration test นั่นเอง เพราะว่า มันไม่ใช่ของจริง แต่บางครั้งอาจจะต้องใช้การ mock เช่นไม่จำเป็นต้องส่ง email และตัดเงินผ่านบัตรเครดิตเป็นต้น Reference Websites https://blog.kentcdodds.com/write-tests-not-too-many-mostly-integration-5e8c7fff591c https://frontendmasters.com/courses/testing-react/testing-trophy/ใช้ private repository ที่ Github แบบฟรี ๆ ได้แล้ว
ตอบคำถามเรื่อง เราจะฝึกแก้ไขปัญหาเชิง programming อย่างไรดี ?
ก่อนอื่นสำหรับการแก้ไขปัญหานั้น คือ การเข้าใจปัญหาก่อน
เนื่องจากผมเชื่อว่า ถ้าเราไม่เข้าใจปัญหาแล้ว เราก็ไม่สามารถแก้ไขปัญหาได้แน่นอน ดังนั้นเมื่ออ่านโจทย์ปัญหา เราต้องอ่านให้ครบ อย่าคิดไปเอง จากนั้นถ้าไม่เข้าใจ ให้ตั้งคำถาม จากนั้นค่อย ๆ หาคำตอบเพื่อให้กระจ่าง แต่ถ้าเข้าใจแล้ว แนะนำให้ลองอธิบายให้คนอื่นเข้าใจ เพื่อเป็นการทบทวนว่า เราเข้าใจมันจริง ๆ และถูกต้องหรือไม่ถ้าเราเข้าใจปัญหาแล้ว วิธีการแก้ไขจะตามมาเอง
เมื่อเข้าใจปัญหาแล้ว ให้เริ่มด้วยการแก้ไขปัญหาแบบ manual หรือคิดบนกระดาษนั่นเอง
ดูเหมือนเป็นเรื่องที่น่าเบื่อ ทำไมไม่เขียน code เลยละ ? แต่เป็นเรื่องที่สำคัญ ผมเชื่อว่า การเขียนบันทึกลงกระดาษหรือลงอะไรบางอย่าง มันช่วยทำให้เราเข้าใจมากยิ่งขึ้น ยิ่งมีข้อมูลตัวอย่างมาเสริมอีก จะยิ่งดีมาก ๆ โดยข้อมูลตัวอย่างนั้น ควรมีทั้ง normal case, conner case และ edge case เสมอ เพื่อช่วยทำให้มั่นใจว่า วิธีการแก้ไขปัญหาของเรามันถูกต้องและครอบคลุม แน่นอนว่าในขั้นตอนนี้เราเริ่มได้ขั้นตอนการคิดและแก้ไขปัญหาแล้ว ทำให้เราได้แผนที่นำทางแล้ว แต่เรายังสามารถปรับปรุงให้มันง่ายและสั้นลงได้อีกเมื่อทุกอย่างเริ่มเข้าที่เข้าทางก็เริ่มเขียน code สิ
หรือบางคนอาจจะเขียนพวก pseudo code ก่อนก็ได้ เนื่องจากเราอาจจะไม่ต้องมาเน้นในเรื่อง syntax ของภาษา แต่ไปเน้นที่ logic และขั้นตอนการทำงานแทน จากนั้นก็แปลงไปเป็น code จริง ๆ แน่นอนว่า ในขั้นตอนนี้เราจะเน้นไปที่ syntax ของภาษานั้น ๆ อย่างเดียว ว่าจะเขียน code อย่างไรในภาษาโปรแกรมที่เราเลือกมาแล้วเมื่อเขียน code เพื่อแก้ไขปัญหาของเราได้แล้ว
ก็อย่าลืมปรับปรุง code ให้มันดีขึ้นด้วย หรือ Clean Code นั่นเอง ทั้งเข้าใจได้ง่าย ทั้งงอ่านง่าย ทั้งทดสอบได้ง่าย ทั้งทำงานครอบคลุมในแต่ละกรณีจากนั้นก็เริ่มเข้าสู่การ review
ทั้ง review จากคนเขียน ทั้ง review จากเพื่อน ทั้ง review จากทีมสุดท้ายคงหนีไม่พ้นคือ การฝึก ฝึกและฝึก นะครับ
ฝึกแก้ไขปัญหาจากที่ได้บ้าง ? ยกตัวอย่างเช่น ขอให้สนุกกับการ coding ครับปล. ให้สังเกตุว่า ในแต่ละขั้นตอนเราจะเน้นที่เรื่องเดียวเท่านั้น
เรียนรู้ภาษา Java ผ่าน Jupyter Notebook
- JDK 9 ขึ้นไป
- Python 3
- Jupyter notebook
Available kernels: java /Library/Frameworks/Python.framework/Versions/3/share/jupyter/kernels/java python2 /Library/Frameworks/Python.framework/Versions/3/share/jupyter/kernels/python3จากนั้นก็เริ่มใช้งานด้วยคำสั่ง $jupyter notebook แสดงการใช้งานดังรูป เพียงเท่านี้ก็ใช้งานได้แล้ว ง่ายมาก ๆ Reference Websites https://blog.frankel.ch/teaching-java-jupyter-notebooks/ https://github.com/SpencerPark/IJava
[Part 1] สรุป 50 เรื่องสำหรับผู้เริ่มต้นพัฒนาระบบด้วยภาษา Go
เรื่องที่ 1 ปีกกาเปิด ( { ) ไม่สามารถอยู่ต่างบรรทัดได้
มิฉะนั้นก็จะ compile ไม่ผ่าน แน่นอนว่า แตกต่างจากภาษาอื่น ๆ อย่างมาก เนื่องจาก Go นั้นจะทำการเพิ่ม selicolon ไปท้ายบรรทัดให้แบบอัตโนมัติ ดังนั้นถ้าเราใส่ { ต่างบรรทัดจึงพังนั่นเอง ดังตัวอย่าง [gist id="8bdf9e24d4efa14e2e95d793e4074c5f" file="1.go"]เรื่องที่ 2 Unused variables
สำหรับภาษา Go นั้น ถ้าทำการประกาศตัวแปรใด ๆ ขึ้นมาแล้วใน function แต่ไม่ถูกใช้งาน สิ่งที่เกิดขึ้นคือ จะ compile ไม่ผ่าน แต่กฏก็มีข้อยกเว้นเช่นกัน ยกตัวอย่างเช่น สามารถประกาศตัวแปรเป็น Global variable ไว้ได้ ถึงแม้จะไม่ถูกเรียกใช้งาน ก็ยัง compile ผ่าน หรือ augment ของ function ต่าง ๆ ถ้าไม่ถูกเรียกใช้งาน ก็ยัง compile ผ่าน มาดูตัวอย่างกัน [gist id="8bdf9e24d4efa14e2e95d793e4074c5f" file="2.go"]เรื่องที่ 3 Unused imports
อีกเรื่องที่สำคัญคือ ถ้าทำการ import library/module ต่าง ๆ เข้ามาใน code แล้วไม่ใช้งาน ผลคือ compile ไม่ผ่านอีกแล้ว แต่ถ้าต้องทำการ import แต่ไม่ได้ใช้งาน ก็สามารถใช้ blank identifier (_) ได้ เพื่อให้ code ที่เขียน compile ผ่าน มาดูตัวอย่างกัน [gist id="8bdf9e24d4efa14e2e95d793e4074c5f" file="3.go"] ในภาษา Go นั้นจะมีเครื่องมือชื่อว่า goimports สำหรับช่วยลบ import ที่ไม่ถูกใช้งานออกไปให้ รวมทั้ง IDE และ Editor ก็มีการจัดการให้ด้วยเรื่องท่ี 4 การประกาศตัวแปรแบบสั้นนั้น ใช้ได้ใน function เท่านั้น (:=)
[gist id="8bdf9e24d4efa14e2e95d793e4074c5f" file="4.go"]เรื่องที่ 5 การประกาศตัวแปรซ้ำด้วยการประกาศแบบสั้น
โดยปกติแล้ว เราไม่สามารถประกาศตัวแปรแบบสั้น ๆ ในตัวแปรเดียวกันได้ แต่สามารถทำได้กับการประกาศตัวแปรมากกว่า 1 ตัว [gist id="8bdf9e24d4efa14e2e95d793e4074c5f" file="5.go"]เรื่องที่ 6 ไม่สามารถค่าให้ field value ของ struct ด้วยการใช้ := ได้
การใช้งาน temporary variable หรือการประกาศตัวแปรไว้ก่อนใช้งานนั้น จะกำหนดค่าได้เฉพาะการกำหนดค่าแบบปกติคือ ใช้งานผ่าน = เท่านั้น [gist id="8bdf9e24d4efa14e2e95d793e4074c5f" file="6.go"]เรื่องที่ 7 ปัญหาที่อาจจะเกิดจาก Variable Shadowing
การประกาศตัวแปรแบบสั้นนั้น อำนวยความสะดวกต่อการเขียน program ในภาษา Go อย่างมาก แต่ถ้าใช้งานไม่ถูกต้อง ก็อาจจะทำให้เกิดปัญหาได้ ถึงแม้จะ compile ผ่านก็ตาม มาดูตัวอย่าง ซึ่งเขียนได้ง่ายมาก ๆ แต่เมื่อเกิดปัญหาจะหายากมาก ๆ [gist id="8bdf9e24d4efa14e2e95d793e4074c5f" file="7.go"] แต่นักพัฒนาสามารถใช้ go vet ในการหาบางปัญหาจาก shadow variable ได้ ค่า default ของ go vet นั้นไม่ได้เปิดความสามารถนี้ ดังนั้น การใช้งานต้องเพิ่ม -shadow flag เข้ามาดังนี้ [gist id="8bdf9e24d4efa14e2e95d793e4074c5f" file="7.txt"]เรื่องที่ 8 ไม่สามารถกำหนด nil ให้กับตัวแปรที่ประกาศแบบไม่กำหนดชนิดของตัวแปรได้
ในภาษา go นั้น nil คือ identifier สำหรับกำหนดเป็น zero-value ให้กับ พวก interface, function, pointer, map, slice และ channel แต่ถ้าทำการกำหนดให้กับตัวแปรที่ไม่กำหนดชนิดของตัวแปรแล้ว สิ่งที่เกิดขึ้นมาคือ compile ไม่ผ่านนั่นเอง เพราะว่าไม่สามารถคาดเดาชนิดของข้อมูลได้เลย [gist id="8bdf9e24d4efa14e2e95d793e4074c5f" file="8.go"]เรื่องที่ 9 การใช้งาน nil กับ Slice และ Map
สามารถเพิ่ม item ใหม่ที่เป็น nil ให้กับตัวแปรชนิด Slice ได้ แต่ไม่สามารถใช้ได้กับตัวแปรชนิด Map ผลที่เกิดขึ้นคือ compile ผ่าน แต่จะเกิด panic ตอน runtime ซึ่งน่ากลัวมาก ๆ ระวังกันด้วย [gist id="8bdf9e24d4efa14e2e95d793e4074c5f" file="9.go"]เรื่องที่ 10 Map capacity ไม่สามารถดึงค่าได้
ในการสร้างตัวแปรชนิด Map นั้น สามารถกำหนด capacity ได้ แต่ไม่สามารถดูค่า capacity ผ่าน function cap() ได้ [gist id="8bdf9e24d4efa14e2e95d793e4074c5f" file="10.go"]เพื่อความปลอดภัย อย่าลืมเปลี่ยน password หรือเปิด Two-Factor Authentication กันนะ
เห็นข่าว Collection #1 is the world's biggest data dump. Check your passwords
เกี่ยวกับข้อมูล email และ password จำนวนกว่า 700 ล้าน account
ถูก hack และปล่อยให้ download แน่นอนว่ามันมี Collection #1
ก็ต้องมี 2, 3, 4 ,5 แน่ ๆ ซึ่งก็มีการเปิดขายกันอีกแน่นอน
ดังนั้น เพื่อความปลอดภัยของเรา ๆ ท่าน ๆ
- แนะนำให้ไปเปลี่ยน password ใหม่
- แนะนำให้ไปเปิดการใช้งาน Two-Factor Authentication ของบริการต่าง ๆ ที่เราใช้งาน
- แนะนำให้ไปปิดบริการที่ไม่ใช้งานซะ
สามารถเข้าไปตรวจสอบเบื้องต้นได้ว่า
Email ที่เราใช้งานนั้นอยู่ในข้อมูลที่โดน hack หรือไม่
ก็ไปตรวจสอบที่ Have I been pawned
ซึ่งเท่าที่อ่านมาเป็น website ที่เชื่อถือได้ ว่าไม่หลอกให้ใส่ email แน่นอน
ปล. บางคนไม่เชื่อก็ไม่ต้องเข้าไปกรอกนะครับ
แต่แนะนำให้ไปทำด้านบนก็น่าจะปลอดภัย
ยกตัวอย่างที่ผมไปตรวจสอบที่ web พบว่า
Email ที่ผมนำไปใช้งานบริการต่าง ๆ ก็เจอ 3 ที่คือ
- Apollo อันนี้ยังนึกไม่ออกว่าไปใช้ตอนไหน สงสัยจะต้อง request demo แต่ไปตรวจสอบที่ web แล้วไม่มี ก็น่าจะ ok
- Dropbox อันนี้ไม่ได้ใช้งานนานน่าดู แต่มีประโยชน์อยู่ เลยไปเปิด Two-Factor Authentication ซะ
- tumblr อันนี้ลืมไปเลยว่าเคยใช้ น่าจะตอนเขียน blog หรือพวก social networking ดังนั้นลบ account ไปซะ
ที่สำคัญกว่าคือ ไปเปลี่ยน password ในแต่ละบริการที่ใช้ซะ
ยิ่งถ้าใครใช้ password เดียวเที่ยวทั่วโลก ยิ่งต้องเปลี่ยนเลย !!
แต่ถ้าบริการไหนที่ใช้ มี Two-Factor Authentication แล้วก็ไปเปิดซะ
โดยไม่ต้องเปลี่ยน password
แต่ถ้ากลัวมาก ๆ ทำทั้งคู่ไปเลย หรือถ้าบริการนั้นไม่ใช้แล้วก็ลบไปซะ
ลองตรวจสอบกันดูนะครับ เพราะว่าเรื่องนี้มันเป็นเรื่องใหญ่และสำคัญมาก ๆ
[Part 2] สรุป 50 เรื่องสำหรับผู้เริ่มต้นพัฒนาระบบด้วยภาษา Go
จาก Part 1 เกี่ยวกับการพัฒนาภาษา Go จำนวน 10 เรื่องแรก
เน้นไปที่การใช้งาน short declaration variable
มาต่อกับ Part 2 อีก 10 เรื่อง เน้นในเรื่องของการใช้งาน String, Array, Slice และ Map
มาเริ่มกันเลย
เรื่องที่ 11 ตัวแปรชนิด string ไม่สามารถกำหนดค่าให้เป็น nil ได้
[gist id="8bdf9e24d4efa14e2e95d793e4074c5f" file="11.go"]เรื่องที่ 12 การส่งข้อมูลชนิด array ผ่าน function
สำหรับนักพัฒนาภาษา C/C++ นั้น ข้อมูลชนิด array คือ pointer
นั่นคือ เมื่อเราส่ง array ไปยัง function
สิ่งเกิดขึ้นคือ เราทำการส่ง pointer หรือตำแหน่งบน memory ไป
ดังนั้น เมื่อ function ทำการเปลี่ยนแปลงค่าของ array แล้ว จะกระทบกับต้นทางเสมอ
แต่ในภาษา go แตกต่าง เพราะว่าการส่งข้อมูลชนิด array ไปยัง function
มันคือการ copy value ของ array
ดังนั้น ถ้าใน function ทำการแก้ไขค่าของ array จะไม่กระทบต้นทางเลย
ซึ่งตรงนี้อาจจะทำให้นักพัฒนาที่เริ่มต้นกับภาษา go สับสนได้ง่าย
แต่ถ้าต้องการให้ใน function ทำการแก้ไขค่า array และต้นทางเปลี่ยนแปลง
ต้องส่ง pointer ของ array เข้ามา ดังนี้
[gist id="8bdf9e24d4efa14e2e95d793e4074c5f" file="12.go"]
หรือสามารถใช้งาน slice ได้เช่นกัน ดังนี้
[gist id="8bdf9e24d4efa14e2e95d793e4074c5f" file="122.go"]
เรื่องที่ 13 ข้อควรระวังในการใช้ range กับ slice และ array
ข้อควรระวังในการวน loop ด้วยคำสั่ง for ซึ่งมักจะใช้ร่วมกับ range
โดยที่ค่าที่ return กลับมาจาก range มี 2 ค่าคือ index กับ value
สิ่งที่ผิดพลาดมาก ๆ คือ ประกาศตัวแปรมารับเพียงค่าเดียว
ซึ่งจะได้ค่า index นั่นเอง ไม่ใช่ค่าของ value !!
[gist id="8bdf9e24d4efa14e2e95d793e4074c5f" file="13.go"]
เรื่องที่ 14 การเข้าถึง key ที่ไม่มีใน Map
ถ้าเป็นภาษาโปรแกรมอื่น ๆ แล้ว
ถ้าเราต้องเข้าถึงหรือดึงข้อมูลจาก Map ด้วย key ที่ไม่ม่ีใน Map
ผลที่น่าจะต้องได้มาคือ nil หรือไม่พบข้อมูลนั่นเอง
แต่ไม่ใช่ในภาษา go
เนื่องจาก Map ในภาษา go จะ return zero value กลับมาเมื่อไม่พบข้อมูล
ค่าที่ return กลับมาสามารถมีค่าเป็น nil, empty string, false และ 0
ดังนั้นค่าที่ return กลับมานี้ ไม่สามารถนำมาใช้ในการตัดสินใจได้
เพราะว่าอาจจะทำให้เกิดข้อผิดพลาดได้
ยกตัวอย่างเช่น value ของ key=two ใน Map มีค่าเป็น empty string
จากนั้นนำ value ที่ได้มาตรวจสอบ อาจจะทำให้เกิดข้อผิดพลาดได้
[gist id="8bdf9e24d4efa14e2e95d793e4074c5f" file="14.go"]
เรื่องท่ี 15 Strings are immutable
นั่นคือตัวแปรชนิด string ไม่สามารถแก้ไขข้อมูลได้
ถ้าเราพยายามแก้ไขข้อมูลบางตำแหน่งใน string แล้ว
ผลที่ได้คือ compile ไม่ผ่าน เพราะว่า string คือ read-only byte slice นั่นเอง
แต่ถ้าเราต้องการแก้ไข สามารถใช้ byte slice แทนได้ ดังตัวอย่าง
[gist id="8bdf9e24d4efa14e2e95d793e4074c5f" file="15.go"]
แต่ code นี้จะไม่สามารถใช้งานได้กับข้อมูลท่ีเก็บข้อมูลมากกว่า 1 byte
ยกตัวอย่างเช่นภาษาไทย และ ภาษาจีน เป็นต้น
ดังนั้นจำเป็นต้องแปลงไปเป็น rune slide ก่อน
[gist id="8bdf9e24d4efa14e2e95d793e4074c5f" file="152.go"]
เรื่องท่ี 16 การแปลงข้อมูลระหว่าง string และ byte slice
ในภาษา go นั้นเมื่อทำการแปลงข้อมูลไปมาระหว่าง string และ byte slice นั้น
เป็นการ copy ข้อมูลกันจริง ๆ ไม่ใช่เป็นการ cast type เหมือนภาษาอื่น ๆ
และไม่เหมือนการ reslice ที่ slice ใหม่ยังชี้ไปยังที่เดียวกันกับ slice ต้นฉบับ
โดยภาษา go นั้นได้ทำการ optimization เรื่องการแปลงข้อมูลไว้อย่างดี
เพื่อลดการจองพื้นที่ในหน่วยความจำ
เรื่องท่ี 17 การเข้าถึงข้อมูลของ string
ในการเข้าถึงข้อมูลแต่ละตำแหน่งของ string จะใช้งาน index operator
ซึ่งจะ return ข้อมูลชนิด byte กลับมา ไม่ใช่ character
[gist id="8bdf9e24d4efa14e2e95d793e4074c5f" file="17.go"]
แต่ถ้าต้องการเข้าถึงแต่ละตำแหน่งของ string
ให้ได้ค่าเป็น character (unicode code point หรือ rune) แล้ว
สามารถใช้งาน for-range ได้
หรือใช้งานผ่าน package unicode/utf8
หรือใช้งานผ่าน package utf8string จะมีfunction At() ให้งาน
หรือทำการแปลง string เป็น slice ของ rune ก็ได้
เรื่องท่ี 18 ค่าใน string ไม่จำเป็นต้องเป็น UTF-8 เสมอไป
โดยที่เราสามารถตรวจสอบค่าใน string ได้ด้วยว่าเป็น UTF-8 หรือไม่
ด้วย function ValidString() จาก package unicode/utf8
[gist id="8bdf9e24d4efa14e2e95d793e4074c5f" file="18.go"]
เรื่องท่ี 19 ความยาวของข้อมูลชนิด string
ในภาษา go นั้นเรื่องความยาวของ string จะต่างจากภาษาอื่น
โดยถ้าใช้งานผ่าน function len() แล้ว ผลที่ได้คือ
จำนวนของ byte ไม่ใช้จำนวนของ character ทำให้ผลลัพธ์ที่ได้ต่างไปดังนี้
[gist id="8bdf9e24d4efa14e2e95d793e4074c5f" file="19.go"]
จาก code ข้างต้นจะได้ความยาวเท่ากับ 3
นั่นหมายความว่าข้อมูลมี 3 byte นั่นเอง
แต่ถ้าต้องการให้ return เป็น 1 ตามจำนวนของ character ที่เห็นจริง ๆ
ต้องใช้งานผ่าน function RuneCountInString() จาก package unicode/utf8 ดังนี้
[gist id="8bdf9e24d4efa14e2e95d793e4074c5f" file="192.go"]
ในเชิงลึกแล้วนั้น function RuneCountInString()
นั้นไม่ได้ return จำนวนของ character
แต่มันคือจำนวนของ rune ซึ่ง 1 character นั้นอยู่บน rune มากกว่า 1 ได้
ยกตัวอย่างเช่น
[gist id="8bdf9e24d4efa14e2e95d793e4074c5f" file="193.go"]
เรื่องท่ี 20 อย่าลืมใส่ comma (,) ใน multi-line slice, array และ map ด้วย มิเช่นนั้นจะ compile ไม่ผ่าน
[gist id="8bdf9e24d4efa14e2e95d793e4074c5f" file="20.go"]
สรุปขั้นตอนการดึงข้อมูลจาก Xiaomi Smart Air Quality Monitor PM2.5 Detector
วันนี้เห็นน้อง ๆ ในบริษัทถือเครื่องวัดค่า PM2.5 มา
ก็เลยถามว่า เราสามารถดึงข้อมูลจากเครื่องมาได้ไหม ?
น้องบอกว่า ได้สิ
ดังนั้นความอยากรู้อยากลองจึงเกิดขึ้น ว่าต้องทำอย่างไรบ้าง ?
มาเริ่มกันเลย
ขั้นตอนที่ 1 ต้องติดตั้ง Mi Home App บนมือถือก่อน
ใช้สำหรับการ config ค่าต่าง ๆ ของเครื่องวัดค่า PM2.5
เครื่องวัดมันคือ Xiaomi Smart Air Quality Monitor PM2.5 Detector
ตรงนี้ไม่ขออธิบายมากนัก เพราะว่าทำตามขั้นตอนใน app ได้เลย
- Login เข้า app
- ในขั้นตอนเลือก region ให้เลือกเป็น Main Chaina (เท่าที่น้องมันบอก แต่ละ region มันไม่เหมือนกันอีก ต้องดูอีกว่าซื้อมาจากไหน ชีวิตดูยากนะ !!)
- ทำการเพิ่ม device
- ขั้นตอนที่สำคัญคือการเลือก WIFI network ซึ่งน้องบอกมาว่า support 2.4GHz เท่านั้น
- เมื่อผ่านแล้วก็ device จะสร้าง WIFI network ใหม่ขึ้นมา ชื่อแนว ๆ นี้ zhimi.airmonitor.v1_xxxx
- ถ้าเจอก็เปลี่ยนมาใช้ เพื่อใช้ติดต่อไปยัง device เท่านี้ก็จบ ซึ่ง app จะทำการ connect และ config ค่าต่าง ๆ ของ device ให้เอง
ขั้นตอนที่ 2 ดึงข้อมูล device ออกมาอย่างไร ?
จากที่น้องบอกมา มีคนเข้าทำไว้ให้แล้วเยอะเลย
หนึ่งในนั้นคือ miio ซึ่งเป็น project ที่พัฒนาด้วยภาษา NodeJS
การใช้งานก็ไม่ยาก แต่เอกสารไม่ update เลย โครตแย่ !!
เพียงเท่านี้ก็ได้ข้อมูล PM2.5 ที่ต้องการแล้ว
แต่ยังไม่พอ เรามาเขียน code เพื่อดึงข้อมูลมาจัดเก็บเพื่อประมวลผล
รวมทั้งเอามาแสดงข้อมูลแบบสวย ๆ กัน ดีกว่า
ขั้นตอนที่ 3 เขียน code เพื่อดึงข้อมูล PM2.5 จาก device
แน่นอนว่าใช้ libary miio นั่นเอง
ถามว่าเขียนง่ายไหม ตอบว่าง่ายนะแต่ถามว่าเอกสารของ library ช่วยไหม
ตอบได้เลยว่าไม่เลย ห่วยสุด ๆ ไม่ update เลย
โดยค่า default แล้วนั้น library นี้จะทำการดึงข้อมูลทุก ๆ 30 วินาที
ลองไปเปลี่ยนให้เร็วขึ้นตามเอกสาร แต่ก็ไม่เปลี่ยน ดังนั้นใช้ค่า default ก็แล้วกัน
เมื่อได้ข้อมูล PM2.5 ทุก ๆ 30 วินาทีแล้ว
ก็ทำการจัดเก็บลงใน data store ให้เรียบร้อยตามความต้องการของเรา
ขั้นตอนที่ 4 จัดเก็บและแสดงผลข้อมูล PM2.5 ของ device
ในตอนที่ทำก็ไม่ได้คิดอะไรมาก ไหลไปเรื่อย ๆ
ในหัวเลยคิดว่าเอาไปเก็บที่ง่าย ๆ เร็ว ๆ
ก็เลยคิดถึง Firebase Realtime Database เลยเนื่องจากฟรี ง่ายและเร็ว
ที่สำคัญคือ ข้อมูลเปลี่ยนแล้วคนดึงข้อมูลได้ข้อมูลล่าสุดทันทีเลยเท่านี้ก็โดนในแล้ว
ก็เลยไปเขียน code เพิ่มเติมนิดหน่อยคือ
- การบันทึกข้อมูล PM2.5 ไปที่ Firebase Realtime Database
- การดึงหรืออ่านข้อมูลจาก Firebase Realtime Database ซึ่งผมเขียน JavaScipt + HTML ทั่วไป และเอาไป deploy บน Firebase Hosting ไปเลย จบงาน !!
ในส่วนนี้เป็น code ตามเอกสารของ Firebase เลย
ไม่มีอะไรมากผลที่ได้ก็ กาก ๆ ประมาณนี้ ใส่สีนิดหน่อย
หมดไป 1 วันสำหรับการลองของใหม่ ๆ ที่ไม่เคยทำ
มันสนุกดีนะ
จากนี้ก็อยู่ที่จินตนาการของเราแล้วว่าจะเอาไปทำอะไร
เช่น ไปเชื่อมต่อกับ Slack และ LINE เลยดีไหม ?
เช่น เก็บข้อมูลแบบ time serie เพื่อเอามาวิเคราะห์ และแสดงผลต่อไป ?
ที่สำคัญ Xiaomi device มันก็สนุกและง่ายดีนะ
[Part 3] สรุป 50 เรื่องสำหรับผู้เริ่มต้นพัฒนาระบบด้วยภาษา Go
ใน Part 3 นี้จะเป็นเรื่องที่ลึกไปอีกขั้น แต่ยังเป็นสำหรับมือใหม่อยู่ !!
ประกอบไปด้วย
- การใช้งาน log.Panic() และ log.Fatal()
- การทำงานของ data structure ต่าง ๆ
- การใช้งาน for-range
- การทำงานของ switch-case
- เริ่มต้นกับ Goroutine และ channel
มาเริ่มกันเลย
อ่าน Part 1 และ Part 2 ก่อนได้
เรื่องที่ 21 ว่าด้วยเรื่องของ log.Fatal และ log.Panic
โดยที่ log package ในภาษา go นั้นจะแตกต่างจากภาษาอื่น ๆ
ซึ่งมีความสามารถมากกว่าเรื่องของ logging เท่านั้น
เนื่องจากถ้าเรียกใช้งาน function ต่าง ๆ จาก log package แล้ว เช่น log.Fatalln()
ผลที่ตามมาคือ จะออกจาก program หรือ terminate ทันที
ดังนั้นนักพัฒนาต้องรู้และใช้งานให้เหมาะสมด้วย
เรื่องที่ 22 การทำงานต่าง ๆ ของ build-in data structure จะไม่ทำงานแบบ synchronous
เนื่องจาก feature จำนวนมากในภาษา go จะสนับสนุน concurency
ดังนั้นเรื่องความถูกต้องของข้อมูลจึงเป็นเรื่องยาก
แต่ถ้าเราต้องการให้ข้อมูลหรือการทำงานถูกต้องแล้ว
แนะนำให้ใช้งาน goroutine และ channel
แต่ถ้าต้องการจัดารเองก็สามารถใช้งาน sync package
เรื่องที่ 23 ข้อควรระวังในการใช้งาน for-range กับข้อมูลชนิด string
การวน loop เพื่อดึงข้อมูลของ string ในแต่ละตัวนั้น
จะทำการ return กลับมา 2 ค่าเสมอ
ค่าที่ 1 คือ index หรือตำแหน่ง
ค่าที่ 2 คือ byte แรกของ character ใน index นั้น ๆ ไม่ใช้ค่าของ character นะ
ดังนั้นถ้า character เก็บค่าที่เป็น multiple rune แล้ว ผลที่ได้จะผิดทันที
ปล. สามารถใช้งาน package norm สำหรับการทำงานกับ character
มาดูตัวอย่างการใช้งาน for-range กัน ซึ่งจะพบว่า byte แรกที่ไม่รู้จักหรือแปลงได้
จะทำการ return ค่า 0xfffd ออกมา
การแก้ไขคือ แปลงข้อมูล string มาเป็น byte slice ก่อนดังนี้
[gist id="34a6e4cde27d29960b064c496749eb0a" file="232.go"]เรื่องที่ 24 การใช้งาน for-range กับข้อมูลชนิด Map ต้องเข้าใจ
สิ่งที่ต้องพึงระวังก็คือ ทุกครั้งที่ใช้ for-range สำหรับดึงข้อมูลจาก Map นั้น
ในแต่ละครั้งจะไม่ได้ข้อมูลที่เรียงลำดับเหมือนกัน นั่นคือจะ random นั่นเอง !!
ปล. แต่ถ้านำ code ชุดนี้ไป run ใน Go Playground แล้วจะได้ผลการทำงานเหมือนกัน เนื่องจากใน Go Playground จะไม่ทำการ recompile ถ้า code ไม่เปลี่ยน
เรื่องที่ 25 ทำความเข้ากับ case ใน switch
ในแต่ละ case ของ switch นั้นจะทำการ break โดย default อยู่แล้ว
ดังนั้นไม่ต้องกลัวปัญหาเรื่องการลืม break อีกต่อไป
แต่ถ้าเราไม่เข้าใจอาจจะทำให้ใช้งานผิดได้
ยกตัวอย่างเช่น
ซึ่งผลที่เราคาดหวังคือ true ทั้งคู่ แต่ผลที่ได้จริง ๆ ไม่ตรง
ซึ่งนี่คือความแตกต่างของภาษา go ที่ต้องเข้าใจ
แต่ถ้าต้องการตรวจสอบทั้ง 2 ค่าจริง ๆ ก็ให้ทำแบบนี้
โดยในแต่ละ case ใส่ได้มากกว่า 1 ค่า
เรื่องที่ 26 การเพิ่มและลดค่าของตัวเลข
ในภาษาอื่น ๆ จะมี ++ และ -- ซึ่งเป็น unary operation
สำหรับการเพิ่มและลดค่าของตัวเลข ใส่ได้ทั้งด้านหน้าและหลัง เช่น ++x และ x++
แต่ในภาษา go นั้นใส่ได้แต่ด้านหลัง
ที่สำคัญไม่สามารถใช้ใน expression ได้อีก ต้องใช้แบบโดด ๆ ดังนี้
เรื่องที่ 27 Bitwise NOT operator จะใช้ ^ นะ ไม่ใช่ ~ (bitwise complement)
ในภาษา go นั้นจะใช้ XOR (^) มาแทน NOT (~) ยกตัวอย่างเช่น
[gist id="34a6e4cde27d29960b064c496749eb0a" file="27.go"]เรื่องที่ 28 Unexported Structure Fields Are Not Encoded
สำหรับ field/property ใน struct ของภาษา go นั้น
ถ้าขึ้นต้นด้วยอักษรตัวเล็กแล้ว (unexport field)
จะไม่สามารถ encode เป็น XML/JSON/GOB หรืออย่างอื่นได้
ดังนั้นหลังจาก encode แล้วมาทำการ decode
จะพบว่าข้อมูลของ field/property ที่ขึ้นต้นด้วยตัวเล็ก จะมีค่าเป็น zero value ดังนี้
เรื่องที่ 29 App Exits With Active Goroutines
โดยปกติการใช้งาน Goroutine นั้น
ระบบงานของเราจะไม่รอจนกว่า Goroutine จะทำงานสำเร็จ
ซึ่งนักพัฒนา go ที่เพิ่มเริ่มต้นมักจะเข้าใจผิด
ว่าระบบงานของเราต้องรอให้ Goroutine สำเร็จก่อนที่ระบบงานจะจบ
แต่ความจริงคือไม่รอ !!
[gist id="34a6e4cde27d29960b064c496749eb0a" file="29.go"]
ผลที่ออกมาคือ
[0] is running
[1] is running
all done!
ผลที่ออกมานั้น ไม่เป้นไปตามที่เราคาดหวัง
เนื่องจากในแต่ละงานที่เรียก function doit() ต้องแสดงคำว่า it done ด้วย
วิธีการแก้ไขปัญหาที่นิยมใช้คือ WaitGroup จาก sync package
ช่วยทำให้ main grouting รอ worker goroutine ต่าง ๆ ไปจนกว่าจะทำงานเสร็จ
ถ้ามีบาง worker ที่ทำงานนานมาก ๆ ก็มีช่องทางในการสงสัญญาณไปหา
เพื่อสั่ง kill หรือหยุดทำงานได้เช่นกัน
หรือจะเปิดทุก ๆ channel ที่เปิดไปยัง worker ก็ได้เช่นกัน
สามารถเขียน code ใหม่ด้วยการใช้งาน WaitGroup ได้ดังนี้
[gist id="34a6e4cde27d29960b064c496749eb0a" file="292.go"]
ปัญหาที่เกิดขึ้นคือ deadlock !! แต่ว่าเกิดจากอะไร ?
จาก code จะพบว่า ในแต่ละ worker อ้างอิงไปยังตัวแปรชนิด WaitGroup ต่างกัน
หรือทำการ copy value ของ wg ต้นฉบับไปเท่านั้นเอง
ดังนั้นเรียกใช้งาน wg.Done() เมื่อทำงานเสร็จแล้ว จึงไม่ส่งผลต่อ main goroutine
ทำให้ main routine ไม่รู้ว่า worker นั้น ๆ ทำงานเสร็จหรือยัง
จึงทำให้ต่างฝ่ายต่างรอ จนเกิด deadlock ขึ้นมา !!
ดังนั้นสามารถแก้ไขปัญหาด้วยการสร้าง channel ไปยัง worker แต่ละตัว
[gist id="34a6e4cde27d29960b064c496749eb0a" file="293.go"]
เรื่องที่ 30 การส่งข้อมูลไปยัง unbuffer channel
ในการใช้งาน unbuffer channel นั้น
ผู้ส่งสามารถส่งข้อมูลไปยังผู้รับได้เรื่อย ๆไปจนกว่า ผู้รับจะเริ่มทำการ process ข้อมูล
นั่นหมายความว่าเมื่อผู้รับทำการ process ข้อมูลแล้ว
จะทำการ block ข้อมูลจากผู้ส่งทันที
ดังนั้นทำให้มีข้อมูลบางตัวที่ไปไม่ถึงผู้รับนั่นเอง
โดยตรงนี้ก็ขึ้นอยู่กับเครื่องที่ทำการ run ระบบงานด้วย !!!
[gist id="34a6e4cde27d29960b064c496749eb0a" file="30.go"]
ผลที่ออกมาในแต่ละครั้งจะไม่เหมือนกันเช่น
[code] $go run 30.go processed: cmd.1 $go run 30.go processed: cmd.1 processed: cmd.2 $go run 30.go processed: cmd.1 processed: cmd.2 processed: cmd.3 [/code]ความสามารถที่น่าสนใจใน Android Studio 3.3
สำหรับ Android developer ตัวจริงน่าจะใช้ Android Studio 3.4 หรือ 3.5 กันไปแล้ว แต่สำหรับคนไม่ชอบการ update แล้ว project fail ทุกครั้ง
ก็คงต้องชอบกับ Android Studio 3.3 ตัวเต็ม ๆ
ซึ่งไส้ในคือ IntelliJ IDE 2018.2.2 รวมไปถึงสนับสนุน Kotlin 1.3.11
ซึ่งมีความสามารถที่น่าสนใจพอควร มาดูใน feature ที่ผมใช้บ่อย ๆ
อย่างแรกถ้าใครสังเกตคือ
ในการเริ่มต้นใช้งานใหม่ ๆ นอกจากจะ import setting ต่าง ๆ จาก version เก่าได้แล้ว ยังมีเรื่องของการลบ directory ของ version เก่า ๆ ด้วยนะ (Delete unused directories)
อันนี้ถือว่าดีเลย เพราะว่าปกติต้องมาเสียเวลาตามไปลบ
เพราะว่า directory เหล่านี้ใช้เนื้อที่บน harddisk เยอะพอควร
เรื่องที่ 2 คือ หน้า wizard ในการสร้าง project
ให้เลือกได้ทั้งภาษา Java หรือ Kotlin
ให้เลือก Minimum API
ให้เลือก Instant app
ให้เลือกว่าจะสนับสนุน Android X เลยไหม
เรื่องที่ 3 การ build ของ gradle ทำได้ดีขึ้น
ทำให้การ build แบบ incremental ดีขึ้น
ถ้าทำการ config ด้วย annotation processor
รวมไปถึงเรื่องของ lazy task configuration นั่นหมายความว่า
ถ้าใน project ของเรามี build variant ทั้ง debug และ release
ดังนั้นถ้าเรากำลังทำการ build ใน debug แล้ว ตัว grade
จะไม่ทำการ initial task ที่เกี่ยวกับ release เพื่อลดเวลาลงไป
นั่นคือฉลาดขึ้นนั่นเอง อะไรที่ไม่ใช้ก็ไม่ต้องไปทำ
อีกอย่างคือ สามารถเลือก sync เฉพาะ build varient
ที่ใช้งานหรือ active อยู่ได้อีกด้วย
แน่นอนว่า feature นี้ยังอยู่ในขั้นของการทดสอบ experiment
ลองไปเปิดใช้งานดูได้ แน่นอนว่าการ sync เร็วขึ้นแน่นอน
เรื่องที่ 4 การทดสอบ
ใน Android Studio ยังคงปรับปรุงการทำงานของ Emulator ให้ดีขึ้นอย่างต่อเนื่อง
นั่นจะทำให้เราสามารถทดสอบ app ได้เร็ว สะดวกขึ้นอย่างมาก
ทั้งการเปิด Emulator ตัวเดียวกันขึ้นมาในหลาย instance ได้
นั่นคือ AVD เดียวสามารถเปิดได้หลายตัวนั่นเอง
ซึ่งปกติทำได้แค่ 1 ต่อ 1
ส่งผลให้ในระบบการทำงานบน Continuous Integration ทำได้เร็วขึ้น
เพราะว่า สามารถทดสอบแบบขนานได้เลย
แต่เครื่องที่ run ก็ต้องมี resource เยอะ ๆ ด้วยละ เพราะว่ากินเยอะเหลือเกิน !!
ในส่วนของ Image ของ Emulator ตัวใหม่คือ Android 9 Pie นั้น
สนับสนุนการเชื่อมต่อ Peer-to-Peer WIFI ด้วย นั่นคือ Emulator
แต่ละตัวสามารถเชื่อมต่อกันได้อีกด้วย
อีกเรื่องของ Emulator คือการบันทึก snapshot ของ Emulator ได้ ทำให้เราเปิด Emulator ได้รวดเร็วขึ้นอย่างมากมายก่ายกอง
เรื่องที่ 5 ปรับปรุงประสิทธิภาพพวก profiling tool
เป็นของที่ขาดไม่ได้เลยสำหรับ Android development ที่สำคัญ
นักพัฒนาก็ต้องใช้งานให้เป็นด้วย
เพื่อดูประสิทธิภาพการทำงานของ app ว่าเป็นอย่างไร
จะได้ปรับปรุงและแก้ไขให้ดีขึ้น
โดยใน version นี้ได้ปรับปรุงประสิทธิภาพการทำงานให้ดีขึ้นไปอีก
ทั้ง memory allocation, CPU และ networking
สุดท้ายสำหรับ Android Developerท ที่ต้องการลองใช้ของใหม่ ๆ ก่อนใคร
ก็ต้องไปที่ Android Studio Preview กันเลย ในตอนนี้มี
- 3.4 beta 2
- 3.5 canary 2
Reference Websites
https://android-developers.googleblog.com/2019/01/android-studio-33.html