পাঠ ১৫ · ৫৮-এর মধ্যে · মডিউল ৪
Home / Courses / Software Engineering Principles & Git / সফটওয়্যার ডিজাইন প্রিন্সিপল

DRY, KISS ও YAGNI প্রিন্সিপল

DRY, KISS & YAGNI principles
৭ মিনিট পড়া মধ্যবর্তী · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • DRY প্রিন্সিপল এবং কেন ডুপ্লিকেট লজিক একটি বাস্তব, সাধারণ বাগের উৎস
  • KISS প্রিন্সিপল এবং সরলতা বনাম জেনারেলিটির সৎ ট্রেড-অফ
  • YAGNI প্রিন্সিপল এবং কেন speculative future-proofing প্রায়ই অপচয়
  • Python-এ একটি ডুপ্লিকেট ডিসকাউন্ট ফরমুলা বাস্তবে ভাঙার এবং তারপর DRY দিয়ে ঠিক করার একটি সম্পূর্ণ, কোড-ভেরিফায়েড ডেমো

১ · তিনটি ক্লাসিক ডিজাইন মাক্সিম

DRY — Don't Repeat Yourself
প্রতিটি জ্ঞান/লজিকের সিস্টেমে একটিমাত্র, দ্ব্যর্থহীন প্রতিনিধিত্ব থাকা উচিত।
KISS — Keep It Simple, Stupid
সমস্যাটি সঠিকভাবে সমাধান করে এমন সবচেয়ে সরল উপায় বেছে নিন।
YAGNI — You Aren't Gonna Need It
যা এখনই সত্যিকারে প্রয়োজন নেই, তা এখনই তৈরি করবেন না।

২ · DRY — জ্ঞানের একক উৎস

DRYDon't Repeat Yourselfপ্রতিটি জ্ঞান/লজিকের সিস্টেমে একটিমাত্র, দ্ব্যর্থহীন প্রতিনিধিত্ব থাকা উচিত। বলে — যদি একই বিজনেস রুল বা ক্যালকুলেশন একাধিক জায়গায় ডুপ্লিকেট থাকে, তাহলে ভবিষ্যতে একটি পরিবর্তন আনতে হলে অবশ্যই প্রতিটি কপি মনে রেখে আপডেট করতে হবে — একটি কপি মিস হয়ে গেলে সেটাই একটি বাস্তব, সাধারণ বাগের উৎস। সমাধান হলো শেয়ার্ড লজিককে একটি একক পুনঃব্যবহারযোগ্য ফাংশন/ক্লাসে বের করে আনা।

৩ · KISS — সরলতা প্রথমে

KISSKeep It Simple, Stupidসমস্যাটি সঠিকভাবে সমাধান করে এমন সবচেয়ে সরল সমাধান বেছে নেওয়ার নীতি, অপ্রয়োজনীয় জটিলতা এড়িয়ে চলা। বলে — সমস্যাটি সঠিকভাবে সমাধান করে এমন সবচেয়ে সরল সমাধান বেছে নিন, অপ্রয়োজনীয় জটিলতা এড়িয়ে চলুন — L03-এর মেইনটেইনেবিলিটি অ্যাট্রিবিউটের সাথে সরাসরি সম্পর্কিত (সরল কোড বোঝা ও রক্ষণাবেক্ষণ করা সহজ)। একটি সৎ, প্রকৃত টেনশন এখানে আছে: সরলতা মাঝেমাঝে জেনারেলিটি/ফ্লেক্সিবিলিটির বিপরীতে ট্রেড-অফ করে — KISS যুক্তি দেয়, যতক্ষণ না বাস্তব সমস্যা সত্যিকারে জটিলতা দাবি করছে, ততক্ষণ সরলতার দিকেই ঝুঁকে থাকা ভালো।

৪ · YAGNI — এখন যা লাগবে তা বানান

YAGNIYou Aren't Gonna Need Itকাল্পনিক ভবিষ্যৎ প্রয়োজনের জন্য এখনই ফাংশনালিটি/ফ্লেক্সিবিলিটি তৈরি না করার নীতি — শুধু বর্তমানে প্রকৃতপক্ষে প্রয়োজনীয় কাজটুকু বানানো। বলে — কাল্পনিক ভবিষ্যৎ প্রয়োজনের জন্য ফাংশনালিটি/ফ্লেক্সিবিলিটি তৈরি করবেন না যা এখনও সত্যিকারে প্রয়োজন নেই — M2/L08-এর অ্যাজাইলের "changing requirements মেনে নেওয়া" মূল্যবোধের সরাসরি সহযোগী: এখন যা প্রয়োজন সেটাই বানান; রিকোয়ারমেন্ট বদলাবেই, তাই speculative future-proofing প্রায়ই একটি এমন প্রয়োজনের জন্য বাড়তি জটিলতা (KISS-এর লঙ্ঘন) যোগ করে, যা হয়তো কখনোই বাস্তবে দেখা দেবে না।

৫ · DRY-লঙ্ঘনের বাস্তব বাগ ঝুঁকি — কোড দিয়ে

একটি ডিসকাউন্ট ফরমুলা তিনটি ভিন্ন ফাংশনে হুবহু কপি-পেস্ট করা আছে। প্রথমে দেখা যাক তিনটিই একমত। তারপর একটি বাস্তবসম্মত পরিস্থিতি সিমুলেট করা হবে — বিজনেস রুল বদলে গেছে, কিন্তু তিনটির মধ্যে মাত্র দুটি জায়গায় আপডেট করা হয়েছে (একটি বাস্তব, সাধারণ ভুল)।

Python
# --- DRY লঙ্ঘন: ডিসকাউন্ট ফরমুলা তিনটি জায়গায় হুবহু কপি-পেস্ট করা ---
def calculate_invoice_total(quantity, unit_price):
    if quantity >= 10:
        discount = 0.15
    elif quantity >= 5:
        discount = 0.10
    else:
        discount = 0.0
    return quantity * unit_price * (1 - discount)

def calculate_quote_total(quantity, unit_price):
    if quantity >= 10:
        discount = 0.15
    elif quantity >= 5:
        discount = 0.10
    else:
        discount = 0.0
    return quantity * unit_price * (1 - discount)

def calculate_cart_preview_total(quantity, unit_price):
    if quantity >= 10:
        discount = 0.15
    elif quantity >= 5:
        discount = 0.10
    else:
        discount = 0.0
    return quantity * unit_price * (1 - discount)


print("=== আপডেটের আগে (তিনটি কপি একমত) ===")
print("Invoice:", calculate_invoice_total(10, 100))
print("Quote:  ", calculate_quote_total(10, 100))
print("Cart:   ", calculate_cart_preview_total(10, 100))


# ব্যবসায়িক নিয়ম বদলে গেছে: ১০+ পরিমাণে discount এখন ১৫% থেকে বেড়ে ২০% -- কিন্তু বাস্তব বাগের মতো,
# তিনটির মধ্যে মাত্র দুটি জায়গায় আপডেট করা হলো (cart preview-তে আপডেট করতে ভুলে যাওয়া হয়েছে)
def calculate_invoice_total(quantity, unit_price):
    if quantity >= 10:
        discount = 0.20  # আপডেট হয়েছে
    elif quantity >= 5:
        discount = 0.10
    else:
        discount = 0.0
    return quantity * unit_price * (1 - discount)

def calculate_quote_total(quantity, unit_price):
    if quantity >= 10:
        discount = 0.20  # আপডেট হয়েছে
    elif quantity >= 5:
        discount = 0.10
    else:
        discount = 0.0
    return quantity * unit_price * (1 - discount)

# calculate_cart_preview_total() আপডেট করতে ভুলে যাওয়া হয়েছে -- এখনও পুরনো 0.15 discount ব্যবহার করছে

print("\n=== আপডেটের পরে (মাত্র ২টি জায়গায় ঠিক করা হলো -- বাস্তব বাগ) ===")
print("Invoice:", calculate_invoice_total(10, 100))
print("Quote:  ", calculate_quote_total(10, 100))
print("Cart:   ", calculate_cart_preview_total(10, 100), " <-- ভুল! এখনও পুরনো ১৫% discount দিচ্ছে")

    
আপডেটের আগে তিনটিই ৮৫০ (১০০০-এর উপর ১৫% ছাড়) দেয়। আপডেটের পরে calculate_invoice_total ও calculate_quote_total সঠিক নতুন মূল্য ৮০০ (২০% ছাড়) দেয়, কিন্তু calculate_cart_preview_total এখনও পুরনো ৮৫০ দেয় — তিনটি জায়গায় একই লজিক ডুপ্লিকেট থাকার কারণে সিস্টেম এখন অসংগত, এবং এই বাগটি শুধু তখনই ধরা পড়বে যখন কেউ কার্ট প্রিভিউ ও ইনভয়েসের মূল্য পাশাপাশি তুলনা করবে।

এবার একই লজিক একটি একক জায়গায় নিয়ে DRY-কমপ্লায়েন্ট সমাধান দেখা যাক:

Python
# --- DRY-কমপ্লায়েন্ট সমাধান: ডিসকাউন্ট লজিক একটিমাত্র জায়গায় ---
def compute_discount_rate(quantity):
    if quantity >= 10:
        return 0.20
    elif quantity >= 5:
        return 0.10
    return 0.0

def dry_invoice_total(quantity, unit_price):
    return quantity * unit_price * (1 - compute_discount_rate(quantity))

def dry_quote_total(quantity, unit_price):
    return quantity * unit_price * (1 - compute_discount_rate(quantity))

def dry_cart_preview_total(quantity, unit_price):
    return quantity * unit_price * (1 - compute_discount_rate(quantity))


print("=== DRY সংস্করণ: তিনটিই compute_discount_rate() থেকে লজিক নেয় ===")
print("Invoice:", dry_invoice_total(10, 100))
print("Quote:  ", dry_quote_total(10, 100))
print("Cart:   ", dry_cart_preview_total(10, 100))


# ব্যবসায়িক নিয়ম আবার বদলে গেলে (ধরুন ১০+ discount এখন ২৫%), শুধু ১টি জায়গায় বদলালেই সব consistent থাকে
def compute_discount_rate(quantity):
    if quantity >= 10:
        return 0.25
    elif quantity >= 5:
        return 0.10
    return 0.0

print("\n=== compute_discount_rate() একবার বদলানোর পরে (এখন সব automatically consistent) ===")
print("Invoice:", dry_invoice_total(10, 100))
print("Quote:  ", dry_quote_total(10, 100))
print("Cart:   ", dry_cart_preview_total(10, 100))

    
এখন compute_discount_rate()-কে একবার ২৫% এ পরিবর্তন করার পরে তিনটি ফাংশনই স্বয়ংক্রিয়ভাবে ৭৫০ (১০০০-এর উপর ২৫% ছাড়) দেয় — সম্পূর্ণ সামঞ্জস্যপূর্ণ, কারণ প্রতিটি ফাংশন একই একক উৎস থেকে ডিসকাউন্ট রেট নিচ্ছে। এই "একবার বদলালেই সব জায়গায় ঠিক" আচরণটাই DRY-এর বাস্তব, প্র্যাকটিক্যাল সুবিধা — কোনো দাবি নয়, উপরের কোড দিয়ে সরাসরি প্রমাণিত।
মূল কথা · Key takeaway

DRY ডুপ্লিকেশনের বাগ-ঝুঁকি দূর করে, KISS অপ্রয়োজনীয় জটিলতা এড়ায়, আর YAGNI বলে যা লাগবে না তা এখনই না বানাতে। তিনটিই একে অপরকে শক্তিশালী করে — একটি DRY-কমপ্লায়েন্ট, KISS-অনুসারী কোডবেস, যা YAGNI মেনে শুধু প্রয়োজনীয় জিনিস ধারণ করে, সবচেয়ে সহজে রক্ষণাবেক্ষণযোগ্য।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ DRY সবসময় "একই দুই লাইন কোড দুইবার লিখবেন না" বোঝায় কি — নাকি এর চেয়ে গভীর কিছু?

এর চেয়ে গভীর — DRY মূলত জ্ঞান/লজিক নিয়ে, শুধু টেক্সট নিয়ে নয়। দুটি ভিন্ন জায়গার কোড দেখতে একই রকম হতে পারে কিন্তু সম্পূর্ণ ভিন্ন বিজনেস কারণে বদলাতে পারে — সেক্ষেত্রে সেগুলো একত্র করা DRY-এর অপব্যবহার (accidental duplication-কে জোর করে একত্র করা, যা ভুলভাবে দুটি অসম্পর্কিত জিনিসকে বেঁধে ফেলে)। আসল DRY প্রশ্নটা হলো — "এই দুই জায়গার লজিক কি একই কারণে বদলাবে?"

প্র ০২ KISS-এর "সরলতা" আর YAGNI-এর "এখন যা লাগবে তা বানান" — এই দুটো নীতি একে অপরকে কীভাবে সমর্থন করে?

একটি সিস্টেমে যদি ভবিষ্যতের কাল্পনিক প্রয়োজনের জন্য আগে থেকেই ফ্লেক্সিবল, জেনারেল-পারপাস কোড লেখা হয় (YAGNI-এর লঙ্ঘন), সেটি প্রায় সবসময়ই বাড়তি জটিলতা যোগ করে (KISS-এর লঙ্ঘন) — এক্সট্রা প্যারামিটার, এক্সট্রা abstraction layer, যেগুলো বর্তমান সমস্যার জন্য অপ্রয়োজনীয়। তাই YAGNI মেনে চলা প্রায় স্বয়ংক্রিয়ভাবে KISS মেনে চলার দিকে নিয়ে যায়।

প্র ০৩ উপরের কোড সেলে যদি calculate_cart_preview_total()-ও আপডেট করা হতো, তাহলে কী পরিবর্তন দেখা যেত — এবং সেটা কেন এখনও DRY-লঙ্ঘন থেকে যায়?

তিনটি ফাংশনই ৮০০ দিত — কোনো দৃশ্যমান বাগ থাকত না। কিন্তু এটি এখনও DRY-লঙ্ঘন, কারণ ডিসকাউন্ট লজিক এখনও তিন জায়গায় ডুপ্লিকেট — শুধু এবার কাকতালীয়ভাবে তিনটিই সঠিকভাবে ম্যানুয়ালি আপডেট করা হয়েছে। ঝুঁকিটা রয়েই যায়: পরের বার কেউ যদি একটি জায়গা মিস করে (যেমন এই লেসনের ডেমোতেই দেখানো হয়েছে), বাগটি আবার ফিরে আসবে — DRY-কমপ্লায়েন্ট সমাধান এই ঝুঁকিটাই কাঠামোগতভাবে দূর করে দেয়।

অনুশীলন

  1. চিন্তা করুন: একজন ডেভেলপার একটি নতুন ফিচারের জন্য এখনই ৫টি ভিন্ন পেমেন্ট গেটওয়ে সাপোর্ট করার একটি জেনারেল, প্লাগইন-স্টাইল আর্কিটেকচার তৈরি করছেন, যদিও প্রোডাক্টটি এখনও মাত্র ১টি গেটওয়ে ব্যবহার করে এবং নিকট ভবিষ্যতে আর কোনো গেটওয়ে যোগ করার পরিকল্পনা নেই। এটি কোন প্রিন্সিপলের লঙ্ঘন এবং কেন?

    এটি YAGNI-এর একটি সরাসরি লঙ্ঘন — একটি কাল্পনিক ভবিষ্যৎ প্রয়োজনের (৫টি গেটওয়ে) জন্য এখনই জটিলতা তৈরি করা হচ্ছে, যদিও বাস্তব বর্তমান প্রয়োজন মাত্র ১টি গেটওয়ে। এটি একই সাথে KISS-ও লঙ্ঘন করে — কারণ একটি অপ্রয়োজনীয় প্লাগইন আর্কিটেকচার কোডবেসে বাড়তি জটিলতা যোগ করে যা বর্তমান সমস্যার জন্য ন্যায্য নয়। ভালো পন্থা: এখন শুধু ১টি গেটওয়ে সরলভাবে ইমপ্লিমেন্ট করা, প্রয়োজন সত্যিকারে দেখা দিলে তখন রিফ্যাক্টর করা।

  2. পরীক্ষা করুন: DRY-কমপ্লায়েন্ট কোড সেলে compute_discount_rate()-এ একটি নতুন টিয়ার যোগ করুন (যেমন quantity >= 20 হলে discount = 0.30) এবং Run চেপে দেখুন তিনটি ফাংশনই সঠিকভাবে নতুন নিয়ম প্রতিফলিত করে কিনা।

    হ্যাঁ — যেহেতু তিনটি ফাংশনই compute_discount_rate() কল করে, নতুন টিয়ার যোগ করার সাথে সাথে তিনটিই স্বয়ংক্রিয়ভাবে নতুন নিয়ম অনুসরণ করবে, কোনো অতিরিক্ত পরিবর্তন ছাড়াই। এটাই DRY-এর মূল প্রতিশ্রুতি — একটি একক পরিবর্তন সব নির্ভরশীল জায়গায় স্বয়ংক্রিয়ভাবে সঠিকভাবে ছড়িয়ে পড়ে।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

আগের পাঠ
কাপলিং ও কোহেশন