লিন্টিং হলো স্ট্যাটিক্যালি কোড অ্যানালাইসিস করার প্রসেস যাতে পটেনশিয়াল সমস্যা খুঁজে বের করা যায়।
এই কেসে কী একটি "সমস্যা" ধরে নেওয়া হবে, সেটা প্রোগ্রামিং ল্যাঙ্গুয়েজ জুড়ে ভিন্ন হতে পারে, অথবা একই ল্যাঙ্গুয়েজের ভিতরেও প্রজেক্ট জুড়ে ভিন্ন হতে পারে। আমি এই সমস্যাগুলো কয়েকটি ক্যাটেগরিতে ভাগ করব:
- প্রোগ্রাম্যাটিক (Programmatic)
- সিকিউরিটি (Security)
- স্টাইলিস্টিক (Stylistic)
- পারফরম্যান্স (Performance)
প্রতিটির কয়েকটি উদাহরণ দেখি।
স্টাইলিস্টিক সমস্যা
কোড স্টাইল করার জন্য কোনো অবজেক্টিভলি সঠিক পথ নেই, কারণ এটা সম্পূর্ণ রিডারের প্রেফারেন্সের ওপর নির্ভর করে। কী হলো কনসিস্টেন্সি (সামঞ্জস্য)। সাধারণ বিতর্কের বিন্দুগুলো:
- ডাবল কোট vs সিঙ্গল কোট
- ট্যাব vs স্পেস
- ম্যাক্সিমাম লাইন লেংথ
- মাল্টিলাইন কলের ইন্ডেন্টেশন, নিচের উদাহরণে দেখানো হলো:
# সবসময় সিঙ্গল-লাইন foo(:a, :b, :c) # প্রথম আর্গুমেন্টের সাথে অ্যালাইন foo(:a, :b, :c ) # ফাংশন নামের সাথে অ্যালাইন foo( :a, :b )
এই সকল বিষয় সম্পূর্ণ সাবজেক্টিভ, কিন্তু সাধারণত প্রতিটি নির্দিষ্ট প্রজেক্টের জন্য একটি স্ট্যান্ডার্ডে একমত হওয়া ফলদায়ক, যাতে পুরো কোডবেস কনসিস্টেন্ট থাকে।
প্রোগ্রাম্যাটিক সমস্যা
এখানে আমি এমন সমস্যাগুলো রাখছি:
- অত্যন্ত লং মেথড বডি, যা রিডেবিলিটি এবং মেইনটেইনেবিলিটি ইস্যু সৃষ্টি করে
- সাইক্লোম্যাটিক কমপ্লেক্সিটি (Cyclomatic Complexity), কোড কমপ্লেক্সিটি মাপার একটি কমন মেট্রিক
- কন্ডিশনের ভিতরে অ্যাসাইনমেন্ট। সবচোয়ে সম্ভব, আপনি যদি
if x = trueটাইপ করেন, তবে আপনি আসলেif x == trueমানেchten। এবং আপনি যদি সত্যিকারের অ্যাসাইনমেন্ট মেনে করেন, তবুও এটি একটি কম ইন্টুইটিভ পদ্ধতি।
সিকিউরিটি সমস্যা
কিছু ফাংশন বা প্র্যাকটিসে পটেনশিয়াল সিকিউরিটি সমস্যা থাকতে পারে যা ডেভেলপাররা নাও জানতে পারেন।
উদাহরণস্বরূপ, রুবিতে, Kernel#open একটি ফ্লেক্সিবল ফাংশন যা ফাইল বা এক্সটার্নাল URL খোলার অনুমতি দেয়। কিন্তু এটি আربিট্রারি ফাইলসিস্টেম এক্সেসও দেয়, অদ্ভুটি কল দিয়ে যেমন open("| ls")। তাই, ডেভেলপারদের এনে সতর্ক করা বুদ্ধিমানের কাজ যাতে তারা সेफার অ্যালটার্নেটিভ ব্যবহার করতে পারে (File#open, IO.popen, URI.parse#open), অথবা নিজের রিস্কে আচরণ রাখতে সিদ্ধান্ত নিতে পারে।
পারফরম্যান্স সমস্যা
রুবির আতরিক কাজকাজ সম্পর্কে অনেক ডিটেইল রয়েছে যা কনটেক্সট অনুযায়ী কিছু অপশনকে অন्यों চেয়ে বেশি পারফরম্যান্ট বানায়।
এই বিষয়গুলো সম্পর্কে লিন্টার সতর্ক করলে, আমরা পথে পথে শিখতে পারি এবং আমাদের প্রোগ্রামের ডিটেইলগুলো অপ্টিমাইজ করতে পারি।
উদাহরণস্বরূপ, Ruby 2.5-এ String#delete_suffix ইন্ট্রোডিউস করা হয়েছে যা একটি সাবস্ট্রিং ডিলিট করে স্ট্রিংয়ের শেষ থেকে। এই দুটি লাইন একুইভ্যালেন্ট, কিন্তু পরেরটিই বেশি পারফরম্যান্ট কারণ এটি জেনেরিক স্ট্রিং রেজেক্স ম্যাচের ওপর নির্ভর করে না:
str = 'string_with_suffix' # খারাপ str.gsub(/suffix\z/, '') # ভালো str.delete_suffix('suffix')
অটো ফিক্সিং (Auto Fixing)
লিন্টারগুলোর একটি গুরুত্বপূর্ণ দিক হলো তাদের সক্ষমতা: খুঁজে বের করা সমস্যাগুলোর কিছু বা সব অটোমেটিক্যালি ফিক্স করা। স্টাইলিং বিষয় যেমন লাইন লেংথ সহজে অটোমেট করা যায়, তাই ডেভেলপারের ওপর থেকে সেই বোঝা হটায়া যাক। অন্যান্য সমস্যা সাবজেক্টিভ হতে পারে বা মানব হস্তক্ষেপ দরকার হতে পারে, যেমন একটি বড় মেথড রিফ্যাক্টর করা। সেসব কেসে, অটোমেশন সম্ভব নয়।
কনভেনশন বা কনফিগারেশন (Convention or Configuration)
একটি কমিউনিটি বা প্রজেক্টের ভিতরে কী রুলগুলো মেনে চলা উচিত সেটা নিয়ে প্রায়শই হিট ডিবেট হয়।
ট্র্যাডিশনাল সমাধান হলো প্রতিটি টিমকে তাদের সদস্যদের মধ্যে ডিবেট সমাধান করার অনুমতি দেওয়া, লিন্টিং রুলগুলো তাদের নিজস্ব পছন্দ অনুযায়ী কনফিগার করার মাধ্যমে। যাক, недавির বছরে, একাধিক ল্যাঙ্গুয়েজে একটি একক কনভেনশনে স্ট্যান্ডার্ডাইজ করার দিকে ঝुकাও দেখা গেল। যদিও এটা সব জায়গায় এনফোর্স করা হয়নি, সাধারণ ধারণা হলো: ডেভেলপাররা কোড স্টাইল করার সময় যে মেন্টাল ওভারহেড ভোগ করেন সেটা সম্পূর্ণরূপে সরিয়ে ফেলা। লাইন লেংথ কী ভালো কাজ করে সেটা নিয়ে আলাপ আলাপ করার বদলে, সবাই শুধু কমিউনিটি-অনুমোদিত রুলগুলো ফলো করে।
রুবিতে, এটি কম-বেশি ترجمه হয় দুটি বিদ্যমান লিন্টারে: RuboCop, যা ফুল-কনফিগারেশন দেয়, এবং StandardRB, যা বিপরীত পদ্ধতি নেয় এবং একটি কমন স্ট্যান্ডার্ড ডিফাইন করে।
RuboCop
সাধারণ পদ্ধতি নেয়: একটি ডকুমেন্টেড রুল সেট প্রদান করে, যেটার প্রতিটা একটি নির্দিষ্ট সমস্যা খুঁজে বের করে। ডেভেলপাররা তাদের প্রজেক্টের ভিতরে নির্দিষ্ট রুলগুলো ডিজেবল বা টিউন করতে পারেন:
# ম্যাক্স অ্যালাওড লাইন লেংথ কনফিগার করুন Layout/LineLength: Max: 80 # সাইক্লোম্যাটিক কমপ্লেক্সিটি ডিজেবল করুন Metrics/CyclomaticComplexity: Enabled: false
এতে ইতিমধ্যে সেন্সিবল ডিফল্টস রয়েছে, তাই কনফিগারেশন শুধু যেসব রুল আপনি চেঞ্জ করতে চান সেগুলোর জন্য দরকার।
bundle exec rubocop রান করলে RuboCop পুরো কোডবেস অ্যানালাইসিস করবে এবং খুঁজে পাওয়া সব ইস্যু লিস্ট করবে:
# test.rb def badName if something return "inner result" end "outer result" end
$ bundle exec rubocop Inspecting 1 file C Offenses: test.rb:1:1: C: [Correctable] Style/FrozenStringLiteralComment: Missing frozen string literal comment. def badName ^ test.rb:1:5: C: Naming/MethodName: Use snake_case for method names. def badName ^^^^^^^ test.rb:2:3: C: [Correctable] Style/IfUnlessModifier: Favor modifier if usage when having a single-line body. Another good alternative is the usage of control flow &&/||. if something ^^ test.rb:3:12: C: [Correctable] Style/StringLiterals: Prefer single-quoted strings when you don't need string interpolation or special symbols. return "inner result" ^^^^^^^^^^^^^^ test.rb:6:3: C: [Correctable] Style/StringLiterals: Prefer single-quoted strings when you don't need string interpolation or special symbols. "outer result" ^^^^^^^^^^^^^^ 1 file inspected, 5 offenses detected, 4 offenses auto-correctable
তারপর bundle exec rubocop --auto-correct রান করলে, আপনার অধিকাংশ ইস্যু আপনার কনফিগারেশন অনুযায়ী ফিক্স হয়ে যাবে।
bundle exec rubocop আপনার CI পাইপলাইনে সেট আপ করলে নিশ্চিত হবে কোনো কোড লিন্টিং রুল পূরণ না করলে পাস করবে না।
StandardRB
একটি আলদা প্রজেক্ট, যা আসলে RuboCop ব্যবহার করে अন্ডার দি হুড। StandardRB-এর মূল লক্ষ্য হলো একটি সম্পূর্ণ আলাদা লিন্টার বানানো নয়, বরং একটি স্ট্যান্ডার্ড পৌঁছানো যা সবাই শুধু ব্যবহার করতে পারে বদলে ডিবেট করার।
যেখানে এটি প্রথম ঘোষণা করা হয়েছিল সেটা লাইটনিং টক খুব স্পষ্ট কারণগুলো ব্যাখ্যা করে: যদি লোকজন সিন্ট্যাক্টিক ডিটেইল নিয়ে আলাপ আলাপ করতে কম সময় খরচ করে এবং কমিউনিটি-ব্যাপী সিদ্ধান্তের অ্যানুসরণ করে, তবে আমরা সবাই আরও বেশি সময় ব্যয় করতে পারব সেটা যা আসলে মেন্টার: চমৎকার প্রোডাক্ট এবং লাইব্রেরি বানানো।
RuboCop যেহেতু নিচে ব্যবহার হচ্ছে, আপনি যে আউটপুট পাবেন সেটা আসলে একই ফরম্যাটে। একমাত্র পার্থক্য: আপনাকে কোনো রুল কাস্টোমাইজ করার অনুমতি নেই।
StandardRB প্রায়শই 1.0.0-এ পৌঁছেছে, অর্থাৎ কোন রুলগুলো ব্যবহার করা উচিত সেটা নিয়ে অধিকাংশ ডিসকাশন ইতিমধ্যে তাদের ইশু পেজে হয়ে গেছে। আপনি যদি কোনো নির্দিষ্ট রুল নিয়ে চিন্তিত হন বা অসম্মত হন, তবে সেখানে সম্পর্কিত ডিসকাশন পাবেন।
궁극적으로, আপনি বিশ্বাস করতে পারেন সেটা কিছু গভীরে আলোচনা হয়েছে। একটি সম্পূর্ণ কমিউনিটির ১০০% স্ম্মতি পাওয়া অসম্ভব সকল পয়েন্টে। এই পদ্ধতির ফিলোসফি হলো: মানুষ ফ্লেক্সিবল এবং তারা অসম্মত হতে পারেন এবং একটি সিদ্ধান্তে কমিট করতে পারেন।
চূড়ান্ত চিন্তা
গত প্রজেক্টগুলোর লিন্টিং রুল নিয়ে নিটপিকিং-এ আমার গর্বের চেয়ে বেশি সময় বিতরণ করার পর, আমি অবশ্যই StandardRB-এর পদ্ধতির মূল্য বুঝতে পারছি এবং যেকোনো সময় সম্ভব হলে এটা ব্যবহার করার পরামর্শ দিই।
সব সুবিধা রাখতে (কনসিস্টেন্সির) ডিসকাশনের ওভারহেড সরিয়ে ফেলার পাশাপাশি, অধিকাংশ রুলের জন্য অটোমেটেড ফিক্স থাকা আমাদের আরও দক্ষতার সাথে ভালো সফটওয়্যার ডেলিভার করতে সাহায্য করে, এবং ফোকাস করতে দেয় সেটা যা আসলে মেনে।
অন্যান্য ল্যাঙ্গুয়েজও সিমিলার লো-কনফিগারেবিলিটি কোড ফরম্যাটার আডপ্ট করছে। Elixir-এ mix formatter, এবং Rust-এ rustfmt — দুটোই কিছু কনফিগারেবিলিটি দেয়, কিন্তু কমিউনিটি আশ্চর্য виде স্ট্যান্ডার্ডের ভিতরে থাকতে রাজি।
সেটা বলা থাকলে, RuboCop এখনও একটি পূরনভাবে বৈধ অপশন যদি আপনি, אירোনিক্যালি, এই সেন্টিমেন্টের সাথে অসম্মত হন।
P.S. Ruby Magic পোস্টগুলো প্রেস থেকে বের होतेই পেতে চাইলে, আমাদের Ruby Magic নিউজলেটার সাবস্ক্রাইব করুন এবং কখনো একক পোস্ট মিস করবেন না!