Lập trình đôi (tiếng Anh: Pair Programming) là kiểu lập trình đòi hỏi hai kỹ sư phần mềm cùng tham gia một nỗ lực lập trình chung trên một máy trạm, nghĩa là chỉ có một màn hình, một bàn phím. Mỗi người thực hiện việc mà người kia hiện không làm. Ví dụ, người này gõ các bộ test đơn vị (unit test), người kia nghĩ về các lớp đầu vào (input) sẽ thỏa mãn bộ test đó; hoặc người này viết mã còn người kia quan sát để hướng dẫn hoặc kiểm lỗi. Người ta khuyên rằng hai người nên luân phiên đổi vai trò, khoảng nửa giờ một lần.

Ưu điểm:
Lập trình đôi được quảng cáo là đem lại các lợi ích sau (theo thứ tự từ lớn tới nhỏ):

  • Tăng kỷ luật làm việc. Những người làm việc theo cặp thường có xu hướng “làm đúng” hơn và ít nghỉ giải lao dài.
  • Mã chương trình tốt hơn. Những người làm việc theo cặp ít có xu hướng chọn giải pháp ngõ cụt mà thường cho ra các thiết kế với chất lượng cao hơn.
  • Duy trì tốt luồng làm việc. Người này có thể người kia xem cả hai đang làm đến đâu. Nếu bị ngắt quãng, một người giải quyết ngắt quãng trong khi người kia tiếp tục làm việc.
  • Nâng cao tinh thần. Với một số lập trình viên, làm việc theo cặp có thể vui vẻ hơn.
  • Sở hữu tập thể đối với mã chương trình. Khi mọi người trong một dự án đều làm việc theo cặp, và các cặp thường xuyên quay vòng, thi ai cũng có kiến thức về toàn bộ mã nguồn.
  • Cố vấn. Kể cả những lập trình viên mới vào nghề, người nào cũng biết một cái gì đó mà người khác không biết. Lập trình đôi là một cách nhẹ nhàng cho việc lan tỏa kiến thức đó.
  • Gắn kết nhóm. Các thành viên trong nhóm gắn bó với nhau nhanh hơn khi làm việc theo cặp. Lập trình đôi có thể khuyến khích sự gắn bó trong nhóm.
  • Ít ngắt quãng hơn. Người ta thường ngại làm đứt quãng một nhóm người (đôi) hơn là ngắt quãng một người đang làm việc một mình.
  • Đòi hỏi ít máy trạm hơn, do hai người dùng chung một máy.

Khuyết điểm

  • Các lập trình viên nhiều kinh nghiệm có thể cảm thấy nhàm chán khi hướng dẫn một lập trình viên ít kinh nghiệm trong một môi trường lập trình đôi.
  • Nhiều kỹ sư thích làm việc một mình hơn, và có thể thấy môi trường lập trình đôi rất cồng kềnh.
  • Khó so sánh được và mất giữa các môi trường đôi và không đôi, do các phương pháp đo năng suất lập trình viên còn đang gây nhiều tranh cãi.
  • Các khác biệt trong phong cách viết mã có thể gây ra xung đột.
  • Trong trường hợp các thành viên trong lập trình có lịch làm việc lệch nhau (điều thường thấy trong các môi trường coi trọng sự cân bằng giữa công việc và cuộc sống), thời gian làm việc theo cặp ít đi, dẫn tới việc tăng thời gian hoàn tất công việc.
  • Khi một công ty đánh giá cao việc làm việc từ xa (làm việc tại nhà), hay khi một nhân viên phải làm việc tại nhà vì lý do nào đó, việc lập trình đôi có thể khó khăn hoặc không thể thực hiện được.
  • Không hiểu ý nhau giữa 2 lập trình viên

Pair programing phù hợp lựa chọn khi nào?
– Pair programing thực sự phù hợp cho các dự án phức tạp, cần nhiều thời gian để làm quen, hiểu dự án. Khi đó có thể ghép cặp 1 thành viên mới và 1 thành viên có kinh nghiệm để vừa hoàn thành mục tiêu sản phẩm, vừa truyền đạt hiểu biết dự án.
- Trong trường hợp sự chênh lệnh về kĩ năng của các thành viên trong team sản phẩm là lớn, nhưng có nhu cầu transfer nhanh các kĩ thuật để làm việc.
– Trong các dự án refactor code, yêu cầu cao về coding convention, pair programing phát huy vai trò, khi luôn có 1 người giám sát thực hiện kĩ thuật.

Bài viết này chúng ta sẽ làm rõ các vấn đề
1. Technical debt là gì?
2. Khi nào gặp?
3. Phòng tránh và hạn chế như nào?
4. Làm sao để thay đổi?

Technical debt là gì? – Khi nào gặp?
Theo dịch thuật, technical debt có nghĩa là nợ kĩ thuật. Diễn giải ra, nợ kĩ thuật là khi vì tính chất, yêu cầu của dự án, khi code, chúng ta:
– Bỏ qua bước thiết kế hệ thống.
– Bỏ qua design pattern.
– Bỏ qua SOLID.
– Bỏ qua coding conventions.
– Thực hiện hotfix mà chưa đánh giá hết rủi ro.
– Nhằm đáp ứng yêu cầu về deadline, chọn giải pháp chưa thực sự tối ưu để giải quyết vấn đề.
– Copy paste thì nhanh hơn là refactor code.
Với khá nhiều “bỏ qua” và cách tiếp cận giải quyết bài toán như vậy. Dự án sẽ tồn đọng các vấn đề về kĩ thuật, lớn dần theo thời gian. Cho đến thời điểm, dự án sẽ lâm vào trạng thái sửa chữa, cập nhật thì mệt mỏi, đập đi xây lại thì không đủ nguồn lực, thời gian, kinh tế.

Phòng tránh, hạn chế bằng cách nào?
Ngoài các yếu tố phòng tránh về kĩ thuật. Developer hay Technical Leader cần có mindset về việc xử lý technical debt.
Mindset này thể hiện ở cách team tư duy về dự án. Luôn nhớ:
– refactor code ngay khi có thể.
– Chậm chắc, chuẩn chỉ, chỉn chu hơn là nhanh, tiềm ẩn rủi ro.
– Luôn có effort cho việc xử lý technical debt trong thời gian làm dự án.
– Quyết liệt với việc có sản phẩm tốt.

Vài lời chém gió, hi vọng mọi người khi làm dự án sẽ không bao giờ va phải dự án nào cần maintain mà có technical debt.

Xuất phát từ mấy vấn đề nên muốn cover lại kiến thức về static và singleton.

– Một là để hiểu rõ thêm vấn đề
– Hai là làm rõ thêm vấn đề

Vậy static có đặc điểm gì? Khi nào nên dùng nó?
Singleton thì sao? Dùng thế nào? Các vấn đề cần lưu ý khi sử dụng?

Ok, xắn ✋ vào tìm hiểu static trước.

Định nghĩa: một class static sẽ không cho bạn tạo một cài đặt. Nghĩa là bạn sẽ không thể sử dụng từ khoá new để tạo object. Muốn sử dụng methods và properties cần khai báo thêm từ khoá static và gọi trực tiếp bằng tên lớp.

Đặc điểm
– Có vùng nhớ riêng, không bị thay đổi.
– Chỉ có một refrence duy nhất trong ứng dụng.
– Do không thể tạo instantiated, nên không cần cấp phát thêm vùng nhớ.
– Life cycle giống application.
– Khởi tạo một lần duy nhất khi chạy chương trình.

Nên dùng khi nào
– Xây dựng các lớp không phụ thuộc vào object. Ví dụ class Utility, lưu các thông tin dùng chung cho toàn bộ ứng dụng.
– Tiết kiệm bộ nhớ.

Ok, giờ tới singleton.

Mình xin được copy lại góc nhìn của anh Hiền blog guru
Vì sao ra đời?
Trong GoF, Singleton được đưa ra với mục đích sau: “Ensure a class only has one instance, and provide a global point of access to it.” – “Đảm bảo một class chỉ có duy nhất một instance, và cung cấp một điểm truy cập duy nhất trên toàn cục tới instance.”

Lợi ích:
Nó cung cấp ý tưởng của việc một instance duy nhất, điều mà chúng ta gặp trong nhiều bài toán thực tế: một ứng dụng được khởi tạo, một cấu hình hệ thống, một logger…

Đặc điểm:
Về mặt runtime
– Tiết kiệm: Không giống như static trong class, object và các giá trị trong đó chỉ được khởi tạo khi cần thiết. Memory và cả CPU đều được tiết kiệm (static khởi tạo ngay khi chạy chương trình).
– Chủ động quản lý: Cho phép chủ động quản lý life cycle, giải phóng khi cần thiết.
Về mặt design
– Có khả năng thừa kế.
– Có thể kết hợp với các partern khác.
- Abstract hơn sử dụng static trong class.

Bàn thêm về singleton

Tư tưởng: giải quyết 2 bài toán khác nhau (dù có vẻ liên quan): “Ensure a class only has one instance, and provide a global point of access to it.”
– Một class có duy nhất một instance;
– Cung cấp một điểm truy cập duy nhất trên toàn cục tới instance.
– Tác giả đã vô tình kèm cả lời giải trong bài toán với giả định: để có một điểm truy cập toàn cục duy nhất thì chỉ có duy nhất một instance được tạo ra từ một class. Bài toán “một điểm truy cập” có thể được giải quyết bởi Facade, Wrapper… không nhất thiết phải là Singleton.
Runtime
– Không “thân thiện” với theading.
Design
– Coupling: Vì là global state nên các thành phần bị gắn chặt với nhau.
– Khó / không thể viết test.
– Viết dễ sai sót, để lại lỗ hổng (xem bài trước Singleton có thực sự dễ?).

Nên dùng như thế nào?
Như vậy, ta thấy rằng đa phần những thứ dở của design pattern này là ở tư tưởng global state. Vậy nên những ai yêu thích functional programming thì sẽ rất anti-pattern này. Cũng đúng thôi, GoF sinh ra cho OOP, không phải FP. Và thời đại của GoF (1994) cũng không quan tâm nhiều tới concurrency – bài toán trở thành rất cơ bản trong thời đại này. Bởi vậy, việc sử dụng Singleton có chút thay đổi. Có 3 điều cần chú ý:

  1. Concurrency: Global state là điều tệ hại cho concurrency. Hãy giảm thiểu tối đa nếu có thể. Global state bẻ cong cách suy nghĩ về luồng và gây mệt mỏi cho việc debug trong concurrency. Nếu bạn muốn thiết kế hệ thống tối ưu hiệu năng và concurrency thì không sử dụng Singleton cũng là một ý hay.
  2. Memory:
    Lưu cái gì? Global state cũng là một ý hay vì khiến việc thiết kế và lập trình dễ dàng hơn, nó chỉ không hay khi bạn không cân nhắc tới nên lưu gì. Rất nhiều thứ có thể nhìn dưới góc độ một instance nếu chúng ta không có khả năng khái quát hoá. Logger là Singleton không? Hay có errorlog, accesslog? Database là single thì lưu cả database? Hay chỉ connection? Hay chỉ connectionString? Lưu ít nhất có thể.
    Lưu khi nào? Nhiều người hay gắn Singleton với life cycle của cả ứng dụng, kèm theo việc lưu trữ nhiều, hoặc giữ strong reference dẫn đến GC không thể hoạt động; sớm muộn gì cũng gây ra memory leak. Khởi tạo muộn nhất có thể, giải phóng sớm nhất có thể.
  3. Language: Cần lưu ý cách sử dụng trong từng ngôn ngữ, mỗi ngôn ngữ khác nhau sẽ có vấn đề khác nhau. Dù design pattern là mức thiết kế song đừng mang nguyên cách cài đặt từ ngôn ngữ này sang ngôn ngữ khác, hãy nhìn vào diagram và đặc trưng ngôn ngữ.

– Bài viết dựa trên môi trường làm việc là MacOs. Vui lòng không làm theo nếu bạn dùng window.
– Yêu cầu kĩ năng research cơ bản.
– Biết sơ qua các khái niệm: docker, kitematic, sqlserver, terminal (lol).

Cài đặt môi trường:
B1: Cài docker và sqlserver, 2 thằng này thì khá rõ để làm gì rồi ha. (hướng dẫn)
B2: Cài Kitematic, để quản lý các container (link)
B3: Chạy docker, chạy kitematic và start sql trong docker.
Lưu ý: Bỏ qua bước 2 và bước 3 nếu đã chạy sqlserver từ bước 1. Tuy nhiên khuyến khích cài đủ, chạy theo các step 1-2-3.

Tạo ứng dụng:
B1: Tạo ứng dụng mới ví dụ: asp-net-core-code-first
B1′: Thêm 2 thư viện Microsoft.EntityFrameworkCore.SqlServerMicrosoft.EntityFrameworkCore.Design
B2: Tạo class Person

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
}

B3: Tạo class PersonContext

public class PersonContext : DbContext
{
   DbSet<Person> Persons { get; set; }

   protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
   {
     optionsBuilder.UseSqlServer("Server=localhost\\sql_server_demo,1433;User=sa;Password=reallyStrongPwd123;Database=PersonDb1");
   }
}

B4: Chạy migrate trong terminal dotnet ef migrations add initial
B5: Update database dotnet ef database update
Done!
Tham khảo: ciclosoftware