Small & Focused

৬ মাস পর নিজের লেখা কোড দেখে যদি নিজেই বুঝতে না পারো যে এটি কী করছে, তবে তার প্রধান কারণ হলো ফাংশন লেখার সময় Small & Focused নীতিটি মেনে না চলা। একটি ভালো ফাংশন হলো একজোড়া চোখের মতো—তাকানোর সাথে সাথেই যেন ব্রেন প্রসেস করে ফেলতে পারে এর ভেতরে কী হচ্ছে।

ক্লিন কোডের দুনিয়ায় একটি ফাংশনের সবচেয়ে গুরুত্বপূর্ণ দুটি বৈশিষ্ট্য থাকে:

  • Small (ছোট): একটি ফাংশনের দৈর্ঘ্য হওয়া উচিত খুবই সংক্ষিপ্ত, সাধারণত ৫-১৫ লাইনের মধ্যে।

  • Focused (সুনির্দিষ্ট): একটি ফাংশন শুধুমাত্র একটি কাজ করবে এবং সেই কাজটাই নিখুঁতভাবে করবে। একে সফটওয়্যার ইঞ্জিনিয়ারিংয়ের ভাষায় Single Responsibility Principle (SRP) বলা হয়। একাধিক কাজ এক ফাংশনে ঢোকালে লজিক পেঁচিয়ে যায় এবং বাগ তৈরি হওয়ার আশঙ্কা বাড়ে।

একটি পারফেক্ট ফাংশন লেখার চেকলিস্ট

ফাংশন লেখার সময় তুমি নিজেকে নিচের প্রশ্নগুলো করতে পারো। এগুলো তোমাকে ক্লিন ফাংশন লিখতে গাইড করবে:

১. ফাংশনের নাম কি Verb (ক্রিয়াপদ) বা Phrase দিয়ে শুরু হয়েছে? ফাংশন মানেই কোনো কাজ বা অ্যাকশন। তাই ফাংশনের নাম সবসময় এমন হওয়া উচিত যা নির্দেশ করে এটি কী "করছে"।

  • খারাপ: def email(): pass (এটি নাউন, কাজ বোঝাচ্ছে না)

  • ভালো: def send_welcome_email(): pass (স্পষ্ট বোঝা যাচ্ছে এটি ইমেইল পাঠাবে)

২. ফাংশনটি কি শুধুমাত্র একটি কাজই করছে? তুমি যদি ফাংশনের কাজ বোঝাতে গিয়ে মনে মনে and, then, also শব্দগুলো ব্যবহার করো, তবে বুঝতে হবে ফাংশনটি একাধিক কাজ করছে।

  • খারাপ: manageUser() (এর ভেতরে ইউজার ভ্যালিডেট, ক্রিয়েট এবং ইমেইল সেন্ড—সব হচ্ছে)।

  • ভালো: validateUser(), createUser(), sendEmail() (কাজগুলোকে আলাদা ফাংশনে ভাগ করা)।

৩. ফাংশনে কি ২টির বেশি Parameter (আর্গুমেন্ট) আছে? ক্লিন কোডের নিয়ম অনুযায়ী ফাংশনে শূন্য বা একটি প্যারামিটার থাকা সবচেয়ে ভালো। দুটির বেশি প্যারামিটার থাকলে কোড পড়া এবং টেস্ট করা কঠিন হয়ে যায়। অনেকগুলো ইনপুট থাকলে সেগুলোকে একটি অবজেক্ট বা মডেলে মুড়িয়ে দেওয়া উচিত (যাকে Parameter Object বলা হয়)।

৪. ফাংশনে কি কোনো Side Effect আছে? Side effect মানে হলো ফাংশনটি তার মূল কাজের বাইরে গিয়ে অন্য কিছু পরিবর্তন করে ফেলছে। যেমন: গ্লোবাল ভ্যারিয়েবল চেঞ্জ করে ফেলা, ফাইলে কিছু রাইট করা বা অপ্রত্যাশিত কোনো ডাটাবেস কল করা। ফাংশনগুলোকে যতটা সম্ভব আইসোলেটেড বা স্বাধীন রাখা উচিত।

৫. Return Early (Guard Clause) ব্যবহার করা হয়েছে কি? নেস্টেড if-else কোডের রিডেবিলিটি নষ্ট করে। ফাংশন যত তাড়াতাড়ি ভুল ইনপুট বা এরর ধরতে পারবে, তত দ্রুত তাকে return করে দেওয়া উচিত। এতে মূল লজিকটি কোনো ইনডেন্টেশন ছাড়াই ফ্ল্যাট থাকে। একে Guard Clause বলা হয়।

৬. ফাংশনটি কি খুব বড় হয়ে গেছে? রুল অফ থাম্ব হলো: কোনো ফাংশন ১৫-২০ লাইনের বড় হয়ে গেলে তোমাকে থামতে হবে এবং ভাবতে হবে— "একে কি আরও ছোট ছোট সাব-ফাংশনে (Helper function) ভাগ করা যায়?"

Parameter Object এর উদাহরণ (Java)

যখন ফাংশনে অনেকগুলো প্যারামিটার দরকার হয়, তখন সেগুলোকে আলাদা না পাঠিয়ে একটি অবজেক্টের মাধ্যমে পাঠানো উচিত।

// Bad: Too many parameters
public void registerUser(String firstName, String lastName, String email, String password, String role) {
    // registration logic
}

// Good: Grouped into a Parameter Object
public void registerUser(UserRegistrationDto userData) {
    // registration logic using userData.getEmail(), userData.getPassword()
}

Return Early বা Guard Clause এর ম্যাজিক (Python)

নেস্টেড লজিক বাদ দিয়ে দ্রুত রিটার্ন করার উদাহরণ:

# Bad: Nested logic, hard to read
def process_order(order):
    if order is not None:
        if order.is_paid:
            save_order(order)
            send_invoice(order)
        else:
            return "Order not paid"
    else:
        return "Invalid order"

# Good: Flat and readable using Return Early
def process_order(order):
    if order is None:
        return "Invalid order"
        
    if not order.is_paid:
        return "Order not paid"
        
    # Main logic is clean and flat
    save_order(order)
    send_invoice(order)

Bad Code বনাম Clean Code: একটি সম্পূর্ণ চিত্র

ধরা যাক, আমরা ইউজার রেজিস্ট্রেশন হ্যান্ডেল করছি। একটি অগোছালো ফাংশন কীভাবে একাধিক কাজ করে এবং ক্লিন কোডে সেটি কীভাবে ভাগ হয় তা নিচে দেখানো হলো:

# Bad Code (Big & Messy - Doing multiple things)
def handle_user_registration(user_data):
    # 1. Validation logic
    if not user_data.get("email") or "@" not in user_data["email"]:
        print("Invalid email format")
        return False
        
    # 2. Database logic
    db_connection = connect_to_db()
    db_connection.save("users", user_data)
    
    # 3. Notification logic
    email_server = setup_email_server()
    email_server.send(user_data["email"], "Welcome to our platform!")
    
    # 4. Role assignment logic
    db_connection.assign_role(user_data["id"], "DEFAULT_USER")
    
    return True


# Clean Code (Small & Focused - Using helper functions)
def handle_user_registration(user_data):
    if not is_valid_email(user_data.get("email")):
        return False

    register_new_user(user_data)
    send_welcome_email(user_data["email"])
    assign_default_role(user_data["id"])
    
    return True

# Helper functions focusing on single responsibilities
def is_valid_email(email):
    return email is not None and "@" in email

def register_new_user(user_data):
    pass # DB logic isolated here

def send_welcome_email(email_address):
    pass # Email logic isolated here

def assign_default_role(user_id):
    pass # Role logic isolated here

এই নিয়মগুলো না মানলে বাস্তবে কী হয়?

১. কোড রিভিউয়ের সময় নষ্ট: অন্য ডেভেলপাররা তোমার বড় ফাংশন দেখে বুঝতে পারে না তুমি ঠিক কী করতে চেয়েছ।

২. বাগ ফিক্স করতে গিয়ে নতুন বাগ তৈরি হয়: ফাংশন অনেক বড় হলে একটি লজিক ঠিক করতে গিয়ে অজান্তেই অন্য লজিক ভেঙে যায়।

৩. টেস্টিং অসম্ভব হয়ে পড়ে: একটি ফাংশন ৪টি কাজ করলে তার জন্য Unit Test লেখা দুঃস্বপ্নে পরিণত হয়।

৪. নতুন ফিচার যোগ করা কঠিন হয়: কোডগুলো tightly coupled (একে অপরের সাথে আষ্টেপৃষ্ঠে জড়িত) থাকায় নতুন কিছু যোগ করতে গেলে পুরো সিস্টেম ব্রেক করার ভয় থাকে।

সামারি (Summary)

তোমার ফাংশনগুলোকে এমনভাবে লেখো যেন সেটি গদ্য বা গল্পের মতো সাবলীলভাবে পড়া যায়।

  • ১টি ফাংশন = ১টি দায়িত্ব = ১টি ফোকাস।

  • বারবার একই স্ট্রাকচার দেখলে তাকে Helper Function এ আলাদা করে ফেলো।

  • ডাটাবেস, ইমেইল বা এপিআই কলের মতো External Side Effect গুলোকে মূল বিজনেস লজিক থেকে আইসোলেট (আলাদা) করে রাখো।

"When code reads like prose, bugs hide no more." — কোড যখন গল্পের মতো পড়া যায়, তখন সেখানে বাগ লুকিয়ে থাকার কোনো জায়গাই থাকে না।