বাইনারি সার্চ ট্রি – প্রোগ্রামিং ইন্টারভিউ সমস্যা ২

সমস্যাঃ একটি ফাংশন তৈরি করতে হবে, যেখানে একটি বাইনারি ট্রি ইনপুট দেওয়া হলে সেটি বাইনারি সার্চ ট্রি (BST) কী না, তা বের করতে হবে।

সমাধানঃ কোনো বাইনারি ট্রি-কে বাইনারি সার্চ ট্রি হতে হলে ওই ট্রি-এর যেকোনো নোডের বামদিকের চাইল্ড ও নাতি-পুতি নোডগুলো ওই নোডের চেয়ে ছোট এবং ডানদিকের চাইল্ড ও নাতি-পুতি নোডগুলো ওই নোডের চেয়ে বড় হতে হবে। যেমন নিচের ট্রি-টি একটি বাইনারি সার্চ ট্রি –

             6
          /     \
         3       12
       /   \    /   \
      1     4  9     13

কিন্তু নিচের বাইনারি ট্রি-টি বাইনারি সার্চ ট্রি নয় (কেন?)

             6
          /     \
         3       12
       /   \    /   \
      1     7  9     13

নোটঃ বাইনারি সার্চ ট্রি নিয়ে আমি বিস্তারিত আলোচনা করেছি, কম্পিউটার প্রোগ্রামিং ৩য় খণ্ড – ডেটা স্ট্রাকচার ও অ্যালগরিদম পরিচিতি এবং পাইথন দিয়ে প্রোগ্রামিং শেখা ৩য় খণ্ড – ডেটা স্ট্রাকচার ও অ্যালগরিদম পরিচিতি বইতে।

এখন আমরা যদি প্রতিটি নোডের বামদিকের নোডটি সেই নোডের চেয়ে ছোট কী না এবং ডানদিকের নোডটি সেই নোডের চেয়ে বড় কী না, সেটি পরীক্ষা করি, তাহলে কিন্তু সমাধান সঠিক হবে না। কেন সঠিক হবে না, সেটি না বুঝলে ওপরে যেই দুটি উদাহরণ দিয়েছি, তা ভালোমতো বুঝতে হবে। তাহলে সমাধান কী? প্রতিটি নোডের বামদিকে যতগুলো নোড আছে, সেগুলো ওই নোডের চেয়ে ছোট কী না এবং তার ডানদিকে যতগুলো নোড আছে, সেগুলো ওই নোডের চেয়ে বড় কী না, তা পরীক্ষা করতে হবে। তাহলে আমরা পাইথনে কোড লিখে ফেলি –

class node:
  def __init__(self, data):
      self.data = data
      self.left = None
      self.right = None


def find_max(root):
    max_v = root.data
    if root.left:
        left_max = find_max(root.left)
        if left_max > max_v:
            max_v = left_max
    if root.right:
        right_max = find_max(root.right)
        if right_max > max_v:
            max_v = right_max
    return max_v


def check_binary_search_tree(root):
    if root is None:
        return True
        
    # find the largest number on the left sub-tree and check if it's smaller/equal to the root
    if root.left:
        max_value = find_max(root.left)
        if max_value >= root.data:
            return False
    
    # find the smallest number on the right sub-tree and check if it's larger than the root
    if root.right:
        min_value = find_min(root.right)
        if min_value <= root.data:
            return False
    
    # now do the same for the sub-trees
    valid_left = check_binary_search_tree(root.left)
    valid_right = check_binary_search_tree(root.right)
        
    return valid_left and valid_right

ওপরে find_min ফাংশনটি আমি ইমপ্লিমেন্ট করলাম না, find_max কীভাবে কাজ করে বুঝলে find_min তৈরি করতে সমস্যা হবে না। এখন প্রশ্ন হচ্ছে, ওপরের ফাংশনটির কমপ্লেক্সিটি কত? ফাংশনটির টাইম কমপ্লেক্সিটি হচ্ছে O(n^2). কীভাবে সেটি বুঝতে না পারলে একটি বাইনারি ট্রি, যেটি কী না বাইনারি সার্চ ট্রি, সেটি নিয়ে অ্যানালাইসিস করলে বুঝতে পারা যাবে (এই লেখার প্রথম উদাহরণের ট্রি-এর মতো)।

আরো ভালো সমাধানঃ আমরা O(n) টাইম কমপ্লেক্সিটিতে সমস্যাটির সমাধান করতে পারি। এজন্য আমরা ট্রি-টি ইনঅর্ডার ট্রাভার্সাল করে নোডগুলো একটি লিস্টে রেখে দেব। তারপরে দেখব যে, ওই লিস্টের সবগুলো উপাদান ছোট থেকে বড় ক্রমে সর্ট করা আছে কী না, যদি না থাকে তাহলে এটি বাইনারি সার্চ ট্রি নয়, অন্যথা এটি একটি বাইনারি সার্চ ট্রি।

def check_binary_search_tree_(root):
    nodes = []
    
    def inorder(root):
        if root is None:
            return
        inorder(root.left)    
        nodes.append(root.data)
        inorder(root.right)
            
    inorder(root)
    
    for i in range(len(nodes)-1):
        if nodes[i] >= nodes[i+1]:
            return False
        
    return True

আমরা কিন্তু সময় বাঁচাতে গিয়ে একটু বেশি জায়গা খরচ করে ফেলেছি। কারণ এখানে আমরা একটি অতিরিক্ত লিস্ট ব্যবহার করেছি। এখন, আমরা যদি একটু চিন্তা করি কিংবা চেষ্টা করি, তাহলে এই অতিরিক্ত জায়গা ব্যবহার না করেও কিন্তু সমস্যাটির সমাধান করা যায়। আমি কোড লিখে দিচ্ছি, তবে আমার কোড দেখার আগে নিজে নিজে কাজটি করার চেষ্টা করা উচিত। আর সমাধান সঠিক হলো কী না, তা যাচাই করা যাবে নিচের যেকোনো একটি লিঙ্কে গেলে –

ওপরের লিঙ্কগুলোতে পাইথন ছাড়াও অন্য ভাষা ব্যবহার করা যাবে।

def check_binary_search_tree(root):
    
    def inorder(root):
        nonlocal last_node
        
        if root is None: 
            return True
        
        left_valid = inorder(root.left)    
        if left_valid is False:
            return False
        
        if root.data <= last_node: 
            return False
        
        last_node = root.data
        
        return inorder(root.right)  
    
    last_node = -1 #assuming all nodes are non-negative
    return inorder(root)

লেখাটি যাদের কাজে আসতে পারে, তাদের সঙ্গে শেয়ার করার অনুরোধ রইলো। ধন্যবাদ।
 

pow(a, n) – প্রোগ্রামিং ইন্টারভিউ সমস্যা ১

সমস্যাঃ একটি ফাংশন তৈরি করতে হবে, যেখানে দুটি ইন্টিজার (a, n) ইনপুট দেওয়া থাকলে ফাংশনটি a^n রিটার্ন করবে। ফাংশনটির রানটাইম হতে হবে O(log n)।

সমাধানঃ আমাদেরকে যেটি করতে হবে, সেটি হচ্ছে pow() ফাংশনটি ইমপ্লিমেন্ট করতে হবে। কিন্তু এতে ঘাবড়াবার কিছু নেই। আমরা পাইথনে খুব সহজে এটি ইমপ্লিমেন্ট করতে পারি।

def pow(a, n):
   result = 1.0
   for _ in range(n):
      result = result * a
   return result

ওপরে যেই ফাংশনটি লিখলাম, সেটির কমপ্লেক্সিটি কত? টাইম কমপ্লেক্সিটি হচ্ছে O(n)। কিন্তু আমাদেরকে বলা হয়েছে O(log n) কমপ্লেক্সিটিতে ইমপ্লিমেন্ট করতে। যদি এটি বলা না থাকতো, তাহলে কিন্তু ওপরের কোড লিখে দিলেই হতো।
আমরা ডিভাইড এন্ড কনকোয়ার পদ্ধতিতে সমস্যাটির সমাধান করতে পারি। সেজন্য আমাদেরকে একটি বিষয় উপলব্ধি করতে হবে। a^n-কে আমরা লিখতে পারি, (a^n/2) * (a^n/2). যেমন, 5^4 হচ্ছে 625. কিন্তু আমরা 5^4-কে লিখতে পারি, (5^2) * (5^2)। এভাবে লিখে লাভ কী হলো? লাভ হচ্ছে, 5^2 এর মান আমাদের দুবার বের করতে হবে না, একবার বের করলেই হবে। কিংবা ধরা যাক, আমাদেরকে 2^32-এর মান বের করতে বলা হলো।

2^32 = (2^16) * (2^16)
2^16 = (2^8) * (2^8)
2^8 = (2^4) * (2^4)
2^4 = (2^2) * (2^2)
2^2 = (2^1) * (2^1)

এখন আমরা জানি, a^0 হচ্ছে 1 আর a^1 হচ্ছে a. তাই রিকার্শন ব্যবহার করে সমস্যাটির সমাধান বের করতে কিন্তু আমাদের তেমন বেগ পেতে হবে না। আর n যদি জোড় সংখ্যা না হয়ে বিজোড় সংখ্যা হতো, তখন আমাদের কী করতে হবে? তখন a^n-কে আমরা লিখতে পারি a^(n-1) * a. n যেহেতু বিজোড় সংখ্যা, n-1 অবশ্যই জোড় সংখ্যা। তাই আমরা আবার আগের মতো আগাতে পারি।

এখন আমি প্রোগ্রাম লিখে ফেলি –

def pow(a, n):
   if n == 0: return 1
   if n == 1: return a 

   if n % 2 == 1:
      return a * pow(a, n-1) 
   else:
      p = pow(a, n/2)
      return p * p

ওপরের pow() ফাংশনটির কমপ্লেক্সিটি হচ্ছে O(log n)। কেন সেটি আর এখানে বিস্তারিত ব্যাখ্যা করলাম না, তবে যারা বাইনারি সার্চের কমপ্লেক্সিটি বোঝে, তাদের এটি বুঝতে কোনো সমস্যা হবে না।

সমস্যাটির সমাধান কিন্তু পুরোপুরি হয় নি। কারণ n যদি ঋণাত্মক হয় তখন কিন্তু প্রোগ্রামটি কাজ করবে না। এটি ঠিকঠাক করার জন্য আমাদেরকে কী করতে হবে?

টিডিডি – টেস্ট ড্রিভেন ডেভেলাপমেন্ট

Test Driven Development (TDD)-এর বাংলা অর্থ হচ্ছে টেস্ট চালিত ডেভেলাপমেন্ট। এখানে টেস্ট বলতে সাধারণত সফটওয়্যার টেস্ট (ইউনিট টেস্ট এবং/অথবা ইন্টিগ্রেশন টেস্ট) এবং ডেভেলাপমেন্ট বলতে সফটওয়্যার ডেভেলাপমেন্ট বোঝানো হয়। এটা হচ্ছে সফটওয়্যার ডেভেলাপমেন্ট করার একটি পদ্ধতি। কিছু কিছু বিষয় আছে, যেগুলো নিয়ে সবাই কথা বলতে ভালোবাসে, সবাই হু-হু করে মাথা নাড়ায় যে এটি খুবই ভালো, কিন্তু কেউ আসলে বাস্তব জীবনে বা নিজের জীবনে সেটা অনুসরণ করে না, টিডিডি এমনই এক জিনিস। সারা পৃথিবীতে অনেক সফটওয়্যার ডেভেলাপার এটি নিয়ে কথা বলে, বিভিন্ন কনফারেন্সে আলোচনা হয়, ব্লগ পোস্ট হয়, কিন্তু খুবই কম সংখ্যক সফটওয়্যার ডেভেলাপার এই পদ্ধতি অনুসরণ করে। তাহলে এই বিষয় নিয়ে লিখছি কেন? কারণ দেশে-বিদেশে ইন্টারভিউতে এটি খুবই কমন একটি প্রশ্ন এবং তাই এই বিষয়ে সবার বেসিক ধারণা থাকা উচিত। তারপরে কেউ ব্যক্তিগতভাবে আগ্রহী হলে সে ইন্টারনেট ঘেঁটে টিডিডি সম্পর্কে আরো জেনে নিবে।

টিডিডি পদ্ধতিতে, যখন কোনো রিকোয়ারমেন্ট দেওয়া হয়, তখন সেটি বিশ্লেষণ করে টেস্ট কেইসগুলো আগে বের করা হয়। তারপরে মূল কোড লেখা হয়। একটি উদাহরণের সাহায্যে বিষয়টি ব্যাখ্যা করি।

ধরা যাক, আমাকে কেউ রিকোয়ারমেন্ট দিলো যে, একটি প্রোগ্রাম লিখতে হবে, যেখানে শিক্ষার্থীদের নম্বর ইনপুট দিলে লেটার গ্রেড আউটপুট দেখাবে। তো একটু আলাপ-সালাপ করে আমি রিকোয়ারমেন্ট বুঝে নিলাম এরকম :

  • ৫০ নম্বরের নিচে পেলে ফেইল, অর্থাৎ F গ্রেড।
  • ৫০ থেকে ৫৯ এর মধ্যে নাম্বার পেলে B গ্রেড।
  • ৬০ থেকে ৬৯ এর মধ্যে নাম্বার পেলে B+ গ্রেড।
  • ৭০ থেকে ৭৯ এর মধ্যে নাম্বার পেলে A- গ্রেড।
  • ৮০ থেকে ৮৯ এর মধ্যে নাম্বার পেলে A গ্রেড।
  • ৯০ কিংবা তার বেশি পেলে A+ গ্রেড।

কেউ ল্যাঙ্গুয়েজ ঠিক না করে দিলে আমি পাইথন ব্যবহার করতেই পছন্দ করি, তাই আমার প্রিয় ল্যাঙ্গুয়েজ পাইথনে আমি শুরুতেই একটি ফাংশন লিখে ফেললাম।

def calculate_grade(marks):
    """ Takes marks as input and returns grade as output """
    return "F"

এখন আমি ফাংশনের জন্য টেস্ট কোড লিখে ফেললাম এরকম :

def test_calculate_grade():
    assert calculate_grade(0) == "F"
    assert calculate_grade(30) == "F"
    assert calculate_grade(49) == "F"

তারপরে টেস্ট রান করবো : pytest grade.py। আউটপুট আসবে সব টেস্ট পাশ।

1 passed in 0.01 seconds

কিন্তু সমস্যা হচ্ছে এভাবে কাজ করলে টিডিডি হবে না। টিডিডির ক্ষেত্রে প্রথমে আমার টেস্ট কোড লিখতে হবে, অর্থাৎ test_calculate_grade() ফাংশনটি লিখতে হবে, সেখানে এক বা একাধিক টেস্ট কেইস থাকতে হবে আর তারপরে calculate_grade() ফাংশনটি লেখা হবে। যেই ব্যাপারটি খুবই গুরুত্বপূর্ণ, সেটি হচ্ছে টেস্ট কেস পাশ করানোর জন্য নূন্যতম যতটুকু কোড লেখা দরকার, আমরা ততটুকু কোডই লিখবো। তাহলে আমি প্রথমে ওপরের test_calculate_grade() ফাংশনটি লিখবো, তারপরে নিচের কোড লিখবো।

def calculate_grade(marks):
    """ Takes marks as input and returns grade as output """
    pass

এবারে টেস্ট রান করাবো (সেই আগের কমান্ড pytest grade.py)। আউটপুট আসবে এমন :

>       assert calculate_grade(0) == "F"
E       assert None == 'F'
E        +  where None = calculate_grade(0)
grade.py:9: AssertionError

তাহলে দেখা যাচ্ছে, প্রথমে আমার কোডে কিছু নেই, তাই 0 নম্বর পাওয়ার পরেও F রিটার্ন না করে None রিটার্ন করছে। তাহলে এই কেস পাশ করানোর জন্য এখন আমি ফাংশনটি আপডেট করবো :

def calculate_grade(marks):
    """ Takes marks as input and returns grade as output """
    return "F"

এবারে টেস্ট রান করলে তিনটি টেস্ট কেইসই পাশ করবে। এখন আমি B গ্রেডের জন্য দুইটি টেস্ট কেইস যুক্ত করবো।

def test_calculate_grade():
    assert calculate_grade(0) == "F"
    assert calculate_grade(30) == "F"
    assert calculate_grade(49) == "F"
    assert calculate_grade(50) == "B"
    assert calculate_grade(59) == "B"

এখন টেস্ট রান করলে আউটপুট আসবে এরকম :

>       assert calculate_grade(50) == "B"
E       assert 'F' == 'B'
E         - F
E         + B

grade.py:12: AssertionError

এখন এই টেস্ট কেস পাশ করাতে হবে। এজন্য আমরা এভাবে প্রোগ্রাম লিখবো :

 def calculate_grade(marks):
    """ Takes marks as input and returns grade as output """
    if marks < 50:
        return "F"
    else:
        return "B"

এখন টেস্ট রান করলে সবগুলো কেস পাশ করবে। এখন বাকী সব কেস যোগ করে ফেলবো।

 def test_calculate_grade():
    assert calculate_grade(0) == "F"
    assert calculate_grade(30) == "F"
    assert calculate_grade(49) == "F"
    assert calculate_grade(50) == "B"
    assert calculate_grade(59) == "B"
    assert calculate_grade(60) == "B+"
    assert calculate_grade(69) == "B+"
    assert calculate_grade(70) == "A-"
    assert calculate_grade(79) == "A-"
    assert calculate_grade(80) == "A"
    assert calculate_grade(89) == "A"
    assert calculate_grade(90) == "A+"
    assert calculate_grade(100) == "A+"

এখন একটা একটা করে টেস্টকেস পাশ করানোর জন্য আমি কোড লিখতে থাকবো, এবং প্রতিটি টেস্ট কেস পাশ করে কি না, সেটি দেখার জন্য প্রতিবার টেস্ট রান করাবো। একসময় আমার কোড সব টেস্ট কেস পাশ করবে এবং তখন সেটি দেখতে নিচের মতো হবে :

 def calculate_grade(marks):
    """ Takes marks as input and returns grade as output """
    if marks < 50:
        return "F"
    elif marks < 60:
        return "B"
    elif marks < 70:
        return "B+"
    elif marks < 80:
        return "A-"
    elif marks < 90:
        return "A"
    else:
        return "A+"

এভাবে দেখতে দেখতে আমার কোডটি সম্পূর্ণ তৈরি হয়ে গেল। টেস্ট ড্রিভেন ডেভেলাপমেন্টকে অনেক সময় টেস্ট ফার্স্ট ডেভেলাপমেন্টও বলা হয়, কারণ এখানে প্রথমে টেস্ট কেস লিখতে হয়। টিডিডি-এর মূল ধারণা নিচের সহজ ছবিটির মাধ্যমেও মনে রাখা যায়:

image_thumb3
Test Driven Development (collected from the Internet)

আশা করি টেস্ট ড্রিভেন ডেভেলামপেন্টের ধারণা অনেকেরই কাজে লাগবে চাকরির ইন্টারভিউতে এবং কেউ কেউ হয়ত তার প্রজেক্টে এর প্রয়োগ শুরু করে দিবে। এখন একটি বিষয়। কেউ যদি ৫৯.৫ বা এরকম নাম্বার পায়, তখন তার গ্রেড কী হবে? এটি রিকোয়ারমেন্টে বলা নাই। কিন্তু পরে জানা গেল যে ভগ্নাংশ হলে সেটিকে সিলিং (ceiling) করতে হবে, মানে ৫৯ এর চেয়ে বড় কিন্তু ৬০-এর চেয়ে ছোট সব সংখ্যাকে ৬০ ধরতে হবে। তাহলে এখন আমরা যদি নিচের টেস্টকেস যোগ করি, তাহলে টেস্ট ফেইল করবে:

assert calculate_grade(59.3) == "B"

এই টেস্ট কেসের মতো আরো টেস্ট কেস তৈরি করা এবং সেগুলো পাশ করানোর কাজটুকু পাঠককে দেওয়া হলো।

লোড টেস্টিং

একটি সফটওয়্যারের লোড টেস্টিং (load testing) বলতে বোঝায়, সফটওয়্যারটি কতটুকু লোড নিতে পারে। লোড টেস্টিংকে আধুনিক বাংলায় অনুবাদ করলে দাঁড়ায় চাপ সামলাও, কিন্তু যেহেতু এটি একটি পরিচিত টার্ম, তাই আমরা লোড টেস্টিংই বলবো। লোড টেস্টিং যে কেবল সফটওয়্যারের হয় এমন নয়, লিফট, গাড়ি, রড ইত্যাদি অনেক কিছুরই লোড টেস্টিং করা হয়। সফটওয়্যার ক্ষেত্রে আবার বিভিন্ন রকম সফটওয়্যারের জন্য লোড টেস্টিং ভিন্ন অর্থ বহন করে। আমি যদি একটি ফাইল প্রসেসিং সফটওয়্যার নিয়ে কাজ করি, তাহলে সেটির জন্য খুব বড় ফাইলে কাজ করতে দিয়ে লোড টেস্টিং করা যায়। একটি ভিডিও প্রসেসিং সফটওয়্যার কত বড় ভিডিও নিয়ে কাজ করতে পারে, সেটিও একটি লোড টেস্ট হতে পারে ওই সফটওয়্যারের জন্য। কিন্তু আমি আলোচনা করবো ওয়েব সার্ভিস বা ওয়েব এপিআই-এর লোড টেস্টিং নিয়ে। এক্ষেত্রে লোড বলতে বোঝাবে, একসাথে কয়টি ক্লায়েন্ট-এর লোড ওই ওয়েব সার্ভিস সামলাতে পারে।

[এই লেখাটি যারা ওয়েব প্রোগ্রামিংয়ের সঙ্গে পরিচিত, তাদের জন্য। যাদের ওয়েব সম্পর্কে একেবারেই ধারণা নেই, তারা দ্বিমিকের ওয়েব কনসেপ্টস্ নামক ফ্রি অনলাইন কোর্সটি করে ফেলতে পারে।]

একসাথে কয়টি ক্লায়েন্ট আমার ওয়েব সার্ভিস ব্যবহার করতে পারে, এই তথ্য বের করে আমি কী করব? ক্লায়েন্ট মানে হচ্ছে ইউজার। তো আমি যদি এমন কিছু তৈরি করি, যেটি হাজার হাজার ইউজার একই সময়ে ব্যবহার করবে, তাহলে আমার আগে থেকে জানা থাকতে হবে যে আমার ওয়েব সার্ভিস একসাথে কতজন ব্যবহার করতে পারবে এবং আমি যদি জানি যে কতজন ক্লায়েন্ট বা ব্যবহারকারী ওয়েব সার্ভিসটি ব্যবহার করবে, আমি সেই অনুযায়ী ব্যবস্থা নিতে পারবো। যেমন আমি যদি আমার স্কুলের পরীক্ষার ফলাফল দেখার করার জন্য একটি সিস্টেম তৈরি করি যেখানে ওয়েব বা মোবাইল অ্যাপ ব্যবহার করে শিক্ষার্থীরা তাদের পরীক্ষার ফলাফল দেখতে পাবে, তাহলে আমার জানতে হবে স্কুলে মোট কতজন শিক্ষার্থী আছে। আর ফলাফল যেহেতু একই সময়ে প্রকাশ করা হবে, তাই সবাই সম্ভবত একই সময়ে অ্যাপ বা ওয়েবসাইট দিয়ে ফলাফল দেখতে চাইবে। স্কুলের শিক্ষার্থীর সংখ্যা যদি ৩০০০ হয়, তাহলে আমার এই অ্যাপ ও ওয়েবসাইটের পেছনে যে ওয়েবসার্ভিসটি আছে, সেটি ওই ৩০০০ ব্যবহারকারীর লোড সামলাতে পারতে হবে। আবার আমি যদি এসএসসি পরীক্ষার ফলাফলের ওয়েবসাইট ও অ্যাপ তৈরি করি, তাহলে সেই সিস্টেমের যে ওয়েব সার্ভিস থাকবে, তাকে একসাথে পনের লক্ষ ব্যবহারকারীর চাপ সামলানোর জন্য প্রস্তুতি নিতে হবে। তাই লোড টেস্ট করাটা খুবই গুরুত্বপূর্ণ।

ধরা যাক, আমার একটা এপিআই আছে, যেখানে শিক্ষার্থীর রোল নাম্বার ইনপুট দিলে পুরো রেজাল্ট আউটপুট দিবে। সেই এপিআইটা আমি লোড টেস্ট করতে চাই। কীভাবে করব?

প্রথম পদ্ধতি হচ্ছে, আমি আমার কম্পিউটার থেকে বিভিন্ন রোলনাম্বার দিয়ে ওই এপিআই কল করতে পারি। একই সময়ে অনেকবার কল করবো কীভাবে? মাল্টিথ্রেডিং ব্যবহার করে আমরা এমন একটি প্রোগ্রাম লিখতে পারি। তবে সেটি আসলে করার দরকার হবে না, কারণ ইতিমধ্যে এরকম অনেক টুলস পাওয়া যায়। আমি যেমন কয়েকমাস আগে Vegeta নামক একটি টুল ব্যবহার করেছি। এটি ওপেন সোর্স তাই সোর্সকোড দেখলে ধারণা করা যাবে যে লোড টেস্টিং টুলস কিভাবে তৈরি করতে হয়। আরেকটি টুল আছে, jMeter নাম (এটিও ওপেন সোর্স), যেটি আরো ৫ বছর আগে জাতীয় বিশ্ববিদ্যালয়ের ফল প্রকাশের কাজের সময় ব্যবহার করেছিলাম। সেই অভিজ্ঞতা জানা যাবে এই লেখায় : ওয়েবসাইট বিপর্যয় ও মুক্তি উপায়

এখন আমি আমার কম্পিউটারে কোনো একটা টুলস ব্যবহার করে লোড টেস্টিং করে ফেললাম। কিন্তু এখানে দুটি সমস্যা আছে:

  1. প্রতিটি কম্পিউটারেরই একসাথে ওয়েব রিকোয়েস্ট করার (বা ওয়েব সার্ভিসকে কল করার) একটি সীমাবদ্ধতা থাকে, একটি নির্দিষ্ট সংখ্যার বেশি রিকোয়েস্ট একসাথে পাঠানো যায় না।
  2. দ্বিতীয় সমস্যা হচ্ছে নেটওয়ার্কের গতির সীমাবদ্ধতা। ঢাকায় আমার বাসার কিংবা অফিসের ইন্টারনেট যথেষ্ট গতিসম্পন্ন নয়।

তাই, আমাকে যেটি করতে হবে, একসাথে অনেকগুলো কম্পিউটার যোগাড় করে একসাথে লোড টেস্টিং শুরু করতে হবে। তাহলে আমি যদি ১০টি কম্পিউটার ব্যবহার করি, তাহলে ১০ গুণ লোড তৈরি করতে পারার কথা। কিন্তু সবাই যদি একই নেটওয়ার্কে থাকে, তাহলে এক্ষেত্রেও আমি নেটওয়ার্কের সীমাবদ্ধতার কারণে আটকে যাবো। তাহলে করণীয় কী?

আমরা ভিপিএস ব্যবহার করে এই লোড টেস্টিংয়ের কাজ করতে পারি। কারণ ভিপিএসগুলো খুবই দ্রুতগতির নেটওয়ার্কে থাকে এবং আমরা আমাদের প্রয়োজনীয়সংখ্যক (১০০টা দরকার হলে ১০০টা) ভিপিএস চালু করতে পারি। জেমিটার (jMeter) সফটওয়্যারটি একসাথে অনেক কম্পিউটারে বা সার্ভারে ইনস্টল করে কনফিগার করা যায় যেন একই সময়েে অনেকগুলো সার্ভার থেকে রিকোয়েস্ট পাঠানো যায়।

ওপরের কাজগুলো বেশ ঝামেলাসাপেক্ষ। তাই আজকাল অনেক সার্ভিস চালু হয়েছে যারা আমার হয়ে লোড টেস্ট করে দিবে। যেমন : loader.io (এরকম আরো আছে, গুগল সার্চ করলেই পাওয়া যাবে)। এসব সার্ভিস তাদের প্রয়োজনমতো কম্পিউটার ব্যবহার করবে, আমার কেবল সার্ভিসটি ব্যবহার করতে জানতে হবে।

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

ইউনিট টেস্টিং

সফটওয়্যার ডেভেলাপমেন্টের একটি গুরুত্বপূর্ণ ধাপ হচ্ছে কোডিং। তো এই কোডিং করার আগে রিকোয়ারমেন্ট সংগ্রহ ও অ্যানালাইসিস করা হয়, আর্কিটেকচার ডিজাইন করা হয়, কোন প্ল্যাটফর্ম, টুলস্ ও প্রোগ্রামিং ল্যাঙ্গুয়েজ ব্যবহার করা হবে, সেই সিদ্ধান্ত নেওয়া হয়। তারপরে প্রজেক্ট যদি বড় হয়, তখন আরো কিছু ধাপ (যেমন ক্লাস ও ইন্টারঅ্যাকশন ডিজাইন) পার হয়ে কোডিং শুরু করা হয়। এখন কোড লেখার সময় একটি গুরুত্বপূর্ণ ব্যাপার হচ্ছে ইউনিট টেস্টিং যেখানে বিভিন্ন ইউনিট টেস্ট করা হয়। এখন ইউনিট মানে কী? ইংরেজি Unit শব্দের বাংলা অর্থ হচ্ছে একক। একটা মডিউলকে একক ধরা যায়, ইন্টারফেসকে একক ধরা যায়, ক্লাসকে একক ধরা যায়, আবার ফাংশনকেও একক ধরা যায়। আমি এখন কথা বলব ফাংশনকে একক ধরে টেস্ট কোড লেখার ব্যাপারে। আমরা যখন বড় প্রোগ্রাম লিখবো, তখন প্রতিটি আলাদা কাজকে আলাদা ফাংশনে লিখব এবং একটি ফাংশন কেবল একটি কাজই করবো। যদি কখনও দেখা যায় যে একটি ফাংশন একাধিক কাজ করছে, তখন বুঝতে হবে যে ফাংশনটি ভেঙ্গে ছোট ছোট ফাংশন লেখা প্রয়োজন। তো এই ফাংশন হচ্ছে ক্ষুদ্রতম ইউনিট এবং এই লেখার বাকি অংশে ইউনিট টেস্ট বলতে আমি এই ফাংশনগুলোর টেস্ট করাই বোঝাবো।

ইউনিট টেস্ট কিভাবে করে? ফাংশন লেখার পরে ওই ফাংশনের জন্য আলাদা একটি ফাইলে আরেকটি ফাংশন লিখতে হবে যেটি বিভিন্ন ইনপুট প্যারামিটারের মাধ্যমে ওই ফাংশনকে কল করবে। তারপরে আউটপুট মিলিয়ে দেখতে হবে যে আমরা যেই আউটপুট আশা করছি আর ফাংশনটি যেই আউটপুট দিচ্ছে – দুটি সমান কী না। ব্যাস, আমাদের ইউনিট টেস্ট হয়ে গেল। ইন্ডাস্ট্রিতে প্রচলিত সব প্রোগ্রামিং ল্যাঙ্গুয়েজেই ইউনিট টেস্ট সহজ করার জন্য বিভিন্ন প্যাকেজ তৈরি করা থাকে। যদিও এসব প্যাকেজ ব্যবহার না করেও ইউনিট টেস্ট করা যায়, যেহেতু প্যাকেজ তৈরি করে দেওয়া আছে, তাই প্যাকেজ ব্যবহার না করার কোনো কারণ নেই। আমরা এখন পাইথনে একটি সহজ উদাহরণ দেখবো।

পাইথনে unittest নামে একটি বিল্ট-ইন মডিউল রয়েছে যেটি ব্যবহার করে ইউনিট টেস্ট করা যায়। কিন্তু আমরা ব্যবহার করবো pytest কারণ এটি ব্যবহার করা অনেক বেশি সহজ। তবে এটি আলাদাভাবে ইনস্টল করতে হয়। কীভাবে ইনস্টল করতে হয়, সেটি ওদের অফিশিয়াল ডকুমেন্টশন থেকে দেখে নিতে হবে, এই লেখা পড়ার জন্য পাইটেস্ট ইনস্টল করার দরকার নেই, পরে করলেও হবে (কারণ এই লেখার উদ্দেশ্য হচ্ছে ইউনিট টেস্টিং সম্পর্কে প্রাথমিক ধারণা দেওয়া)। আরেকটি জিনিস জানতে হবে, সেটি হচ্ছে assert স্টেটম্যান্ট। assert এর পরে কোনো কিছু লিখলে পাইথন সেটা চালিয়ে দেখে এবং ফলাফল হয় True না হয় False হয়। ফলাফল False হলে পাইথন AssertionError এক্সেপশন দেয়।

ধরা যাক, আমাকে একটি প্রোগ্রাম লিখতে বলা হলো, যেটি ইনপুট হিসেবে একটি বছর নেবে এবং বছরটি লিপ ইয়ার কী না, সেটি বলে দেবে। লিপ ইয়ার হলে True আর লিপ ইয়ার না হলে False রিটার্ণ করবে। আমি জানি যে, কোনো সালকে যদি 4 দিয়ে ভাগ করলে ভাগশেষ শূণ্য হয়, তাহলে সেটি লিপ ইয়ার। তো আমি ঝটপট পাইথনে সেটি লিখে ফেললাম :

def is_leap_year(year):
        """This functon returns True if year is a leap year, returns False otherwise"""
        if year % 4 == 0:
                return True
        return False

এখন এই ফাংশনের জন্য ইউনিট টেস্ট লিখব :

def test_is_leap_year():
        assert is_leap_year(2016) == True
        assert is_leap_year(2015) == False

আমি আমার প্রোগ্রাম leapyear.py নামে সেভ করলাম। এখন pytest রান করাবো।

 tamimshahriar$ pytest leapyear.py 

======= test session starts========

platform darwin -- Python 3.5.1, pytest-3.0.3, py-1.4.31, pluggy-0.4.0

rootdir: /Users/tamimshahriar/work/practice/pypractice, inifile: 

collected 1 items 

leapyear.py .

======= 1 passed in 0.01 seconds =====

ওপরে দেখতে পাচ্ছি যে আমার টেস্ট ঠিকঠাকভাবে পাশ করেছে, কোনো সমস্যা নেই। এখন আমি খোঁজখবর নিয়ে জানলাম যে 2100 সাল নাকি আসলে লিপইয়ার না, কারণ সালটা যদি 100 দিয়ে বিভাজ্য হয়, সেটা 400 দিয়েও বিভাজ্য হতে হবে। তাহলে আমি এই টেস্ট কেইসটি আমার টেস্টে যোগ করে আবার টেস্ট রান করবো। এখন আমার টেস্ট ফাংশনটি হবে এরকম :

def test_is_leap_year():
        assert is_leap_year(2016) == True
        assert is_leap_year(2015) == False
        assert is_leap_year(2100) == False

এখন আবার টেস্ট রান করি : pytest leapyear.py, আউটপুট আসবে এরকম :

========= FAILURES ===========
_______ test_is_leap_year __________

    def test_is_leap_year():
        assert is_leap_year(2016) == True
        assert is_leap_year(2015) == False
>       assert is_leap_year(2100) == False
E     assert True == False
E     +  where True = is_leap_year(2100)

leapyear.py:10: AssertionError

===== 1 failed in 0.03 seconds =====

কোন টেস্ট কেইস ফেইল করেছে সেটা একটা তীরচিহ্ন দিয়ে দেখানো হয়েছে। এখন আমি আমার অরিজিনাল ফাংশনের কোড ঠিক করলে টেস্ট কেস পাশ করবে (পাঠকদের সেটি করার পরামর্শ দেওয়া হলো)।

ইউনিট টেস্ট করার সময় বিভিন্ন ধরনের কেস টেস্ট করা উচিত। ইউনিট টেস্টের সুবিধা হচ্ছে :

  • যে কেউ কোড দেখার আগেই ইউনিট টেস্ট দেখে ধারণা করতে পারবে যে ফাংশনটা আসলে কিভাবে ব্যবহার করতে হয়।
  • ইউনিট টেস্টের কেসগুলো দেখলে বোঝা যায় যে কোনো বিশেষ কেস মিস হয়ে গেল কী না আর তখন সেই কেস যোগ করে টেস্ট রান করলে বোঝা যাবে সেই কেসের জন্য ফাংশনটি ঠিকভাবে আউটপুট দিচ্ছে কী না।
  • অন্যকেউ যদি ফাংশনে কোনো পরিবর্তন করে তাহলে অনেক সময় ফাংশনে অনাকাঙ্ক্ষিত বাগ চলে আসতে পারে। সেক্ষেত্রে যদি ফাংশনের ইউনিট টেস্ট ঠিকঠাক লেখা থাকে, তাহলে বাগ ঢুকে যাওয়ার সম্ভাবনা অনেক কমে যায়। কারণ বাগ ঢুকলে ইউনিট টেস্ট ফেইল করবে (যদি ওই কেসটি আগে থেকে ইউনিট টেস্টে লেখা থাকে)।

এই লেখায় ইউনিট টেস্টের একেবারে প্রাথমিক বিষয়গুলো লেখলাম। ইউনিট টেস্ট ঠিকভাবে লেখা শিখতে অনেক সময়, ধৈর্য্য ও পরিবেশের প্রয়োজন। আমি এখন এটি ভালোভাবে শেখার চেষ্টা করছি। ভবিষ্যতে এই বিষয়ে আরো লিখার ইচ্ছা রইল। আরেকটা শেষ কথা বলা প্রয়োজন যে, ইউনিট টেস্ট লেখা কিন্তু ডেভেলাপারের কাজ ও দায়িত্ব, সফটওয়্যার টেস্টিং কিংবা কোয়ালিটি অ্যাশিউরেন্স টিমের কারো নয়।

প্রোগ্রামিং ইন্টারভিউঃ কোডিং

সফটওয়্যার ডেভেলাপার বা ইঞ্জিনিয়ার নিয়োগের ক্ষেত্রে যেই ইন্টারভিউ হয়, সেখানে একটি কমন জিনিস হচ্ছে কোডিং স্কিলের পরীক্ষা। এর জন্য সাধারণত এক বা একাধিক (মোটামুটি সহজ ধরণের) প্রোগ্রামিং সমস্যা দেওয়া হয়, যেটা নিজের পছন্দমতো কোনো ল্যাঙ্গুয়েজে সলভ করা যায়। এখন ইন্টারভিউ শেষে দেখা যায়, প্রার্থী বেশ খুশি, কারণ তার ইন্টারভিউ খুব ভালো হয়েছে। কিন্তু যিনি ইন্টারভিউ নিয়েছেন, তিনি অতটা খুশি নন। তার কারণ আছে। একটি সহজ উদাহরণ দিয়ে ব্যাখ্যা করি। ধরা যাক, ইন্টারভিউতে বলল, দুইটা সংখ্যা ভাগ করার প্রোগ্রাম লিখেন। তখন তুমি মনে মনে “ওয়াও, এত সহজ কাজ আবার ইন্টারভিউতে দেয়?” চিন্তা করে বললে, “আমি পাইথনে কোড লিখব”। তারপরে ঝটপট নিচের কোড লিখে ফেললে :

x = input()
y = input()
print x / y

তখন ইন্টারভিউয়ার তোমাকে বলল, “আপনি একটা ফাংশন লিখে কাজটা করেন”। “আচ্ছা, ঠিকাছে” বলে তুমি নিচের মতো কোড লিখে ফেললে –

def division(x, y):
    return x / y

x = input()
y = input()
print division(x, y)

এবার তুমি পরিতৃপ্ত, তোমার কোড দেখতে বেশ সুন্দর হয়েছে। কিন্তু ইন্টারভিউয়ারের চেহারা দেখে বোঝা যাচ্ছে, তিনি এখনো তেমন খুশি নন। তখন তিনি জিজ্ঞাসা করলেন, আচ্ছা, y-এর মান যদি 0 হয়?

একথা বলতে না বলতেই তুমি ঝট করে নিচের কোড টাইপ করে মুচকি হাসি দিলে:

def division(x, y):
    try:
        return x / y
    except ZeroDivisionError:
        return "Can not divide by zero"

x = input()
y = input()
print division(x, y)

তুমি মনে মনে ভাবছ, “যাক, এবারের কোড বুলেট প্রুফ”। ইন্টারভিউয়ার এবারে বললেন, আচ্ছা, x-এ 5 আর y-তে 2 ইনপুট দিলে কী হবে? পাইথন (2 সিরিজে)-এ সেটার উত্তর হবে 2। কিছুক্ষণ চিন্তাভাবনা করে তুমি তোমার কোড একটু পরিবর্তন করে নিচের মতো করে লিখলে –

def division(x, y):
    try:
        return x * 1.0 / y
    except ZeroDivisionError:
        return "Can not divide by zero"

x = input()
y = input()
print division(x, y)

এরপর আর এই কোড নিয়ে কোনো প্রশ্ন রইল না। ইন্টিজার ও রিয়েল নাম্বারের জন্য এই কোড কাজ করবে। তুমি ইন্টারভিউ দিয়ে খুশিমনে বাড়ি ফিরে গেলে। কিন্তু কয়েকদিন পরে ইমেইল পেলে যে ওরা তোমাকে নিচ্ছে না। কারণ এই ছোট কোড ঠিকভাবে লিখতে যদি এত সাহায্যের প্রয়োজন হয়, তাহলে আরেকটু বড় কাজ তোমার হাতে দেওয়ার ভরসা ঠিক তোমার টিম লিডার করতে পারবেন না (এই কথা অবশ্য ইমেইলে লেখা থাকবে না)। আর হ্যাঁ, ওপরের কোডে ভ্যারিয়েবলের নামও আরো ভালোভাবে দেওয়া যেত। x-এর বদলে numerator বা num এবং y-এর বদলে denominator বা denom। কারণ অর্থপূর্ণ ভ্যারিয়েবল নামকরণও অনেক গুরুত্বপূর্ণ জিনিস। সাথে এক লাইন কমেন্ট যোগ করে দিলে ইন্টারভিউয়ার আরো খুশি হতেন।

def division(numerator, denominator):
    """ Divides numerator by denominator. In case the denominator is    zero, it returns None 
    """
    try:
        return numerator * 1.0 / denominator
    except ZeroDivisionError:
        print "Can not divide by zero"
        return None

আশা করি তোমরা ইন্টারভিউতে প্রশ্ন শুনেই কোডিংয়ে ঝাঁপিয়ে পড়বে না। তাই তোমার জন্য টিপস্ হচ্ছে –

  • প্রশ্ন বুঝেছ কী না, চিন্তা করবে, কোনো জিজ্ঞাসা থাকলে প্রশ্ন করবে,
  • প্রোগ্রামের কর্নার কেসগুলো চিন্তা করবে এবং যথাযথ কোডিং করবে,
  • ভ্যারিয়েবলের নামকরণ ভালোভাবে করবে
  • দরকার হলে কমেন্টও লিখবে

 

সফটওয়্যার ইঞ্জিনিয়ারিং ইন্টারভিউ

সফটওয়্যার ইঞ্জিনিয়ারিং ক্যারিয়ার নিয়ে ধারাবাহিক তবে বিক্ষিপ্ত একটি সিরিজের প্রথম লেখা এটি। আজকের লেখার মূল বিষয় হচ্ছে ইন্টারভিউতে কোন বিষয়গুলোর ওপর জোর দেওয়া হয়, সেই সম্পর্কে ধারণা দেওয়া। তুমি কোন দেশের নাগরিক, কোন বিশ্ববিদ্যালয় থেকে পাশ করা – সফটওয়্যার ইঞ্জিনিয়ার হিসেবে চাকরি করার জন্য এসব তেমন গুরুত্বপূর্ণ ব্যাপার নয়। যাদের প্রোগ্রামিং ও প্রবলেম সলভিং স্কিল খুব ভালো, তাদের জন্য পৃথিবীর বড় বড় কোম্পানীগুলোর দুয়ার খোলা। আর এরকম কোম্পানী কেবল একটি-দুটি নয়, বরং শতশত কোম্পানী।

ইন্টারভিউ খুব গুরত্বপূর্ণ এ কারণে যে সফটওয়্যার ইঞ্জিনিয়ারিং জবের জন্য তোমার সার্টিফিকেট ও অন্যান্য অভিজ্ঞতা (যেমন প্রোগ্রামিং কনটেস্ট) তোমাকে কেবল ইন্টারভিউ পর্যন্ত পৌঁছাতে সাহায্য করবে। ইন্টারভিউ-তে তুমি কেমন করলে, সেটিই নির্ধারণ করবে যে ওই কোম্পানী তোমাকে নেবে কী না।ইন্টারভিউতে ভালো করতে হলে চাই আলাদা প্রস্তুতি। সাধারণত একটি ইন্টারভিউ হয় ৪০ মিনিট থেকে ৬০ মিনিট। এর বেশিরভাগ সময়ই ব্যায় হয় প্রোগ্রামিং প্রবলেম সলভ করার পেছনে। তো আমরা জেনে নিই যে কোন পাঁচটি বিষয়ের ওপর ইন্টারভিউতে জোর দেওয়া হয়।

১) প্রবলেম সলভিং স্কিলঃ যখন ইন্টারভিউতে তোমাকে একটি প্রবলেম সলভ করতে দেওয়া হয়, তখন দেখা হয় যে তুমি প্রবলেমটি ঠিকভাবে বুঝলে কী না। না বুঝলে প্রয়োজনীয় প্রশ্ন করে সবকিছু পরিষ্কার করে নিলে কী না। সমস্যাটি তুমি কিভাবে বিশ্লেষণ করলে? এটি করতে তোমার কী পরিমাণ সময় লাগল? তোমাকে কতটুকু হিন্টস্ দিতে হয়েছে। এসব বিষয় গুরুত্বপূর্ণ।

২) কোডিং স্কিলঃ যখন তুমি সমস্যা বিশ্লেষণ করে অ্যালগরিদম দাঁড় করালে, সেটির কমপ্লেক্সিটি বের করলে, তারপরে কোনো একটি প্রোগ্রামিং ল্যাঙ্গুয়েজে (সেটা সাধারণত তোমার ইচ্ছামতো) সেই অ্যালগরিদমটি কোড করতে বা ইমপ্লিমেন্ট করতে তোমার কেমন সময় লাগল, ঠিকঠাক ইমপ্লিমেন্ট করতে পারলে কী না – এটিও খুব গুরুত্বপূর্ণ। কোডিং স্টাইল কেমন – ভ্যারিয়েবলের নাম ঠিকঠাক দিলে কী না, কোডিংয়ে ইনডেনটেশন ঠিকমতো করা হয়েছে কী না – এগুলোও বেশ দরকারি জিনিস। আমি নিজে যখন ইন্টারভিউ নেই, তখন কেউ ঠিকমতো কোড ইনডেন্ট না করলে তাকে বাদ দিয়ে দিই।

৩) কম্পিউটার সায়েন্সের মৌলিক জ্ঞানঃ কম্পিউটার সায়েন্সের বেসিক বলতে আমরা বুঝি ডাটা স্ট্রাকচার ও অ্যালগরিদম, ডিজিটাল লজিক, অপারেটিং সিস্টেম, ডাটাবেজ, নেটওয়ার্কিং – এসব মৌলিক বিষয়ের ওপর নূন্যতম ধারণা। তোমাকে বিটওয়াইজ অপারেশন জানতে হবে, থ্রেড ও প্রসেসের পার্থক্য জানতে হবে, রেস কন্ডিশন বুঝতে হবে, রিলেশনাল ডাটাবেজ কী, কেন, নোএসকিউএল কেন প্রয়োজন, এগুলো জানা থাকতে হবে।

৪) অভিজ্ঞতাঃ অভিজ্ঞ প্রার্থীদের ক্ষেত্রে পূর্বে যেসব কোম্পানীতে কাজ করেছ, সেগুলো কেমন, তুমি কোন অংশে কাজ করেছ, সেখানে তোমার অবদান কী – এসব বিষয় বেশ খুঁটিয়ে দেখা হয়। আর ফ্রেশ গ্র্যাজুয়েটদের ক্ষেত্রে প্রোগ্রামিং কনটেস্ট, প্রজেক্ট এসবের অভিজ্ঞতা দেখা হয়। ভার্সিটিতে হয়ত তুমি প্রজেক্ট অন্য কাউকে দিয়ে করিয়ে পার হয়ে যেতে পারবে কিন্তু ইন্টারভিউতে এসে ঠিকই ধরা খাবে। এর জন্য প্রস্তুত থেকো।

৫) সফট স্কিলঃ তোমার টেকনিক্যাল স্কিলের বাইরে তুমি এমনিতে মানুষ কেমন, আচার-ব্যবহার, যোগাযোগের দক্ষতা (কমিউনিকেশন স্কিল) এসবও যাচাই করা হয় (কিন্তু কখন এটা করা হচ্ছে, সেটি তুমি টের পাবে না)। অভিজ্ঞ ইন্টারভিউয়ার কিন্তু ইন্টারভিউ শেষে প্রার্থী সম্পর্কে বেশ কিছু প্রশ্নের উত্তর নিজের মনেই দেয়, সবচেয়ে দরকারি প্রশ্ন হচ্ছে, “আমি কি আমার টিমে এই ব্যক্তির সাথে কাজ করতে খুব খুশি হব?” – উত্তর যদি “না” হয়, তবে চাকরি পাওয়ার কোনো সম্ভাবনা নেই।

সবশেষে বলি, গ্র্যাব (grab.com)-এ আমার বস অরুল (তিনি আগে ১৬ বছর মাইক্রোসফটে ও ৩ বছর অ্যামাজনে কাজ করেছেন) আমাকে ইন্টারভিউ নেওয়ার ক্ষেত্রে একটি সহজ টিপস্ দিয়েছেন। ইন্টারভিউ শেষে নিজেকে প্রশ্ন করতে হবে, “আমার বর্তমান টিমের বেস্ট প্রোগ্রামারকে অন্য টিমের জন্য ছেড়ে দিয়ে এই ছেলেকে (বা মেয়েকে) আমার টিমে নিতে কি আমি রাজি?”