শনিবার বিকেল। আপনি কফি নিয়ে বসেছেন, আপনার পরিকল্পনা হলো দ্রুত একটি ফিচার শেষ করা অথবা অবশেষে সেই সাইড প্রজেক্টটি পলিশ করা। দশ মিনিট পর সবকিছু থেমে গেল। লজিক খুব জটিল বলে নয়। আপনি ফ্রেমওয়ার্কটি বুঝতে পারছেন না বলে নয়। প্রগতি থেমে গেল কারণ একটি ট্যাগ খোলা অবস্থায় রয়ে গেছে।
এই উইকএন্ড চ্যালেঞ্জে ঠিক এটাই ঘটেছিল। একটি Liquid সিনট্যাক্স এরর। ট্যাগটি সঠিকভাবে বন্ধ করা হয়নি। পার্সার ফাইলটি পড়ে যখন একটি ক্লোজিং সিকোয়েন্স আশা করছিল, তখন কিছুই পেল না। আর এভাবেই বিল্ড ফেইল করল। এটি এমন এক ধরণের বাগ যা অভিজ্ঞ ডেভেলপারদেরও অপ্রস্তুত করে দেয় এবং নতুনদের আত্মবিশ্বাসের অভাব তৈরি করতে পারে, যদিও এটি একবার দেখে ফেললে ঠিক করতে মাত্র কয়েক সেকেন্ড লাগে।
পর্দার আড়ালে আসলে কী ঘটেছিল
Liquid হলো Shopify-এর তৈরি একটি টেমপ্লেটিং ল্যাঙ্গুয়েজ, যা ই-কমার্স স্টোরফ্রন্ট থেকে শুরু করে GitHub Pages-এর Jekyll-ভিত্তিক ব্লগ পর্যন্ত সবকিছু পরিচালনা করে। এটি দুটি মূল সিনট্যাক্স প্যাটার্নের ওপর নির্ভর করে। ডাবল কার্লি ব্রেস (double curly braces) আউটপুট হ্যান্ডেল করে, যেমন {{ page.title }}। কার্লি ব্রেস পারসেন্ট সাইন (curly brace percent signs) লজিক এবং ফ্লো কন্ট্রোল হ্যান্ডেল করে, যেমন {% if user %} বা {% for item in list %}।
প্রতিটি ওপেনিং ট্যাগের একটি সঙ্গী থাকা প্রয়োজন। একটি {% if %} এর জন্য {% endif %} প্রয়োজন। একটি {% for %} লুপের জন্য {% endfor %} প্রয়োজন। একটি ক্যাপচার ব্লকের জন্য {% endcapture %} প্রয়োজন। এগুলো কোনো পরামর্শ নয়। Liquid ইঞ্জিন আপনার টেমপ্লেট ক্রমানুসারে (sequentially) পড়ে। যখন এটি একটি ওপেনিং কনস্ট্রাক্ট পায়, তখন এটি তার ইন্টারনাল স্ট্যাকে একটি ফ্রেম পুশ করে এবং অপেক্ষা করে। যদি ফাইলটি শেষ হয়ে যায়, অথবা প্রত্যাশিত ট্যাগটি আসার আগেই অন্য কোনো বড় ব্লক বন্ধ হয়ে যায়, তবে ইঞ্জিন এরর থ্রো করে। এর মেসেজটি প্রায়ই খুব সরাসরি হয়: tag was not closed correctly। সিস্টেম একটি ক্লোজিং সিকোয়েন্স আশা করছিল। মাঝে মাঝে আপনি একটি লাইন নম্বর পান। মাঝে মাঝে সেই লাইন নম্বরটি ভুল জায়গা নির্দেশ করে কারণ পার্সার সবকিছু হজম করার পরেই বুঝতে পারে যে তার সঙ্গীটি নেই।
একটি বাস্তব উদাহরণ বিবেচনা করুন। আপনি হয়তো এমন কিছু লিখছেন:
{% for product in collections.all.products %}
<div class="card">
<h2>{{ product.title }}</h2>
{% if product.available %}
<span>In stock</span>
{% endif %}
</div>
{% endfor %}
তিনটি ট্যাগই বন্ধ করা আছে। এখন কল্পনা করুন আপনি দ্রুত কাজ করছেন, ডকুমেন্টেশন থেকে স্নিপেট কপি-পেস্ট করছেন এবং ভুলবশত শেষ r টি বাদ দিয়ে ফেলেছেন:
{% for product in collections.all.products %}
<div class="card">
<h2>{{ product.title }}</h2>
{% if product.available %}
<span>In stock</span>
</div>
{% endfo %}
অথবা হয়তো আপনি {% endfor %} পুরোপুরি ভুলে গেছেন কারণ এটি অনেকগুলো HTML কোডের নিচে ছিল। ইঞ্জিন {% for % দেখে লুপটি রেজিস্টার করে, কিন্তু তার সঙ্গীটি আর খুঁজে পায় না। Shopify-এর ক্ষেত্রে এর মানে হলো পুরো থিমটি কম্পাইল হতে ব্যর্থ হয়। Jekyll-এর ক্ষেত্রে, GitHub Pages আপনাকে একটি বিল্ড ফেইলর ইমেল পাঠাবে। লোকাল ডেভেলপমেন্টে একটি রহস্যময় স্ট্যাক ট্রেস (stack trace) দেখা দিতে পারে। একটি ভুলে যাওয়া ট্যাগ পুরো পাইপলাইন থামিয়ে দিতে পারে।
ছোট ভুলের অত্যাচার
এই এররগুলো অত্যন্ত বিরক্তিকর কারণ ভুলের আকারের সাথে এর প্রভাবের কোনো সামঞ্জস্য নেই। আপনি ডেটাবেস ভুলভাবে ডিজাইন করেননি। আপনি ভুল অ্যালগরিদম বেছে নেননি। আপনি কেবল একটি ক্যারেক্টার ভুলে গেছেন। ছোট ভুল বড় বাগ তৈরি করে। সেই হারিয়ে যাওয়া {% endif %} কেবল একটি লাইনকে ভদ্রভাবে ভেঙে দেয় না; এটি একটি ক্যাসকেড (cascade) বা চেইন রিঅ্যাকশন তৈরি করে। পার্সার এখন কন্ডিশনালটি কোথায় শেষ হয়েছে তা নিয়ে বিভ্রান্ত হয়ে পড়ে এবং এর নিচের প্রতিটি লাইনকে ভুল বা ত্রুটিপূর্ণ হিসেবে গণ্য করতে পারে। যা দেখতে বিশ লাইনের একটি টেমপ্লেট মনে হচ্ছিল, তা হঠাৎ ষাট লাইনের এরর আউটপুট তৈরি করতে পারে, যার বেশিরভাগই বিভ্রান্তিকর।
আপনি যখন একটি মাত্র ক্যারেক্টার ভুলে যান, তখন এই এররগুলোর মুখোমুখি হন এবং আপনার মস্তিষ্ক এই বাস্তবতার জন্য প্রায় কখনোই প্রস্তুত থাকে না। মানুষ প্যাটার্ন রিকগনিশনের মাধ্যমে কোড পড়ে। আমরা উদ্দেশ্যটি বুঝতে পারি। আমরা if এবং তার সাথে মিল থাকা লজিকটি দেখি এবং সীমানাটি অনুমান করি। কম্পিউটার অনুমান করে না। এটি প্রতিটি ক্যারেক্টার ধরে, উপর থেকে নিচে, কোনো অস্পষ্টতা ছাড়াই পড়ে। যখন এটি একটি সঙ্গী ট্যাগের অপেক্ষায় ফাইলের শেষ প্রান্তে পৌঁছে যায়, তখন এটি হাল ছেড়ে দেয়। আপনার কাজ হলো এমন একজন ডেভেলপার হওয়া যিনি গ্যাপটি শনাক্ত করার জন্য যথেষ্ট সময় পর্যন্ত পার্সারের মতো করে চিন্তা করতে পারেন।
এটি কেবল Liquid-এর ক্ষেত্রে নয়। Python-এ একটি বন্ধ না হওয়া প্যারেন্থেসিস (parenthesis), Markdown-এ একটি মিসিং ব্যাকটিক (backtick), JavaScript-এ একটি ভুলে যাওয়া ব্রেস (brace), বা HTML-এ একটি ঝুলে থাকা অ্যাঙ্গেল ব্র্যাকেট (angle bracket)। উইকএন্ড চ্যালেঞ্জটি শেখানোর মাধ্যম হিসেবে Liquid ব্যবহার করেছে, কিন্তু এর অন্তর্নিহিত শিক্ষা আপনি যে কোনো ল্যাঙ্গুয়েজেই পাবেন। সিনট্যাক্স হলো ব্যাকরণ, আর ব্যাকরণ কোনো ক্ষমা করে না।
কীভাবে এগুলো খুঁজে বের করবেন
যখন আপনি এই বাধার সম্মুখীন হন, তখন প্রথম প্রবৃত্তি হলো আতঙ্কিত হয়ে পুরো ফাইলটি পড়া। এটি করবেন না। আতঙ্কিত হয়ে পড়লে আপনি ঠিক সেই ক্যারেক্টারটিই এড়িয়ে যাবেন যা আপনি মিস করেছেন, কারণ আপনার মস্তিষ্ক এটিকে নিজে থেকেই ঠিক করে নেয় (autocorrects)। পরিবর্তে, পদ্ধতিগতভাবে (systematically) কাজ করুন।
আপনার ট্যাগগুলো স্পষ্টভাবে মিলিয়ে নিন। ফাইলটি ভালো করে দেখুন এবং প্রতিটি ওপেনিং ট্যাগ জোরে জোরে বা কাগজে লিখে নাম বলুন। for-এর জন্য endfor প্রয়োজন। if-এর জন্য endif প্রয়োজন। unless-এর জন্য endunless প্রয়োজন। capture-এর জন্য endcapture প্রয়োজন। আপনি যদি ব্লক নেস্টিং (nesting) করেন, তবে মনে মনে একটি কাউন্টার বাড়িয়ে নিন। যখন আমি একটি for-এর ভেতরে একটি if ওপেন করি, তখন ফাইলটি শেষ হওয়ার আগে আমার দুটি দায়বদ্ধতা পূরণ করতে হবে।
আপনার এডিটর ব্যবহার করুন। আপনি যদি নিয়মিত Liquid নিয়ে কাজ করেন, তবে এমন একটি সিনট্যাক্স হাইলাইটার ইনস্টল করুন যা এর গ্রামার চিনতে পারে। Visual Studio Code-এ এমন কিছু এক্সটেনশন আছে যা Liquid ট্যাগগুলোকে হালকা করে দেবে বা কালার-কোড করবে। যখন কোনো ক্লোজিং ট্যাগ ভুলভাবে লেখা হয়, তখন কালার প্যাটার্ন বদলে যায়। কিছু লিন্টার (linter) কম্পাইল করার আগেই বন্ধ না হওয়া ব্লকগুলো শনাক্ত করতে পারে। Vim বা Neovim ব্যবহার করলে vim-liquid-এর মতো প্লাগইন ব্যবহার করার কথা ভাবতে পারেন অথবা ম্যাচিং ট্যাগ হাইলাইট করার জন্য Tree-sitter কনফিগার করতে পারেন। এই টুলগুলো চিন্তা করার প্রয়োজনীয়তা কমিয়ে দেয় না, তবে এগুলো অমিলগুলোকে দৃশ্যমান করে তোলে।
আপনার টেমপ্লেটে বাইনারি সার্চ করুন। যদি এরর মেসেজটি ২০০ নম্বর লাইনের দিকে নির্দেশ করে কিন্তু সেখানে কোনো ভুল না দেখায়, তবে আসল অপরাধী সম্ভবত তার উপরের কোনো লাইনে আছে। টেমপ্লেটের নিচের অর্ধেক অংশ কমেন্ট আউট (comment out) করে দিন। এবার কি বিল্ড হচ্ছে? যদি হয়, তবে এররটি কমেন্ট করা অংশে আছে। এবার সেই অংশের অর্ধেক আনকমেন্ট করুন। যতক্ষণ না আপনি ত্রুটিপূর্ণ ব্লকটি আলাদা করতে পারছেন, ততক্ষণ এটি পুনরাবৃত্তি করুন। এটি ধীরগতির মনে হতে পারে, কিন্তু আপনার বিরক্তি বাড়তে বাড়তে একই ২০০ লাইন ছয়বার পড়ার চেয়ে এটি অনেক দ্রুত।
আপনার ইনক্লুড (includes) গুলো পরীক্ষা করুন। Liquid {% include %} বা {% render %} এর মাধ্যমে মডুলার ফ্র্যাগমেন্ট সাপোর্ট করে। বন্ধ না হওয়া ট্যাগটি হয়তো মূল ফাইলে নেই। এটি কোনো স্নিপেটের (snippet) ভেতরে থাকতে পারে যা প্যারেন্ট টেমপ্লেট থেকে আনা হয়েছে। এখানেই ভার্সন কন্ট্রোল আপনার মানসিক প্রশান্তি বজায় রাখতে সাহায্য করে। একটি diff চালান। শেষ সফল বিল্ডের পর কী কী পরিবর্তন হয়েছে তা দেখুন। প্রায়ই লাল এবং সবুজ রঙের মধ্যেই উত্তরটি চোখে পড়ে যায়।
ইনডেন্টেশন (Indentation) হলো ডকুমেন্টেশন। যদি আপনার {% if %} কলাম জিরো থেকে শুরু হয় এবং এর সংশ্লিষ্ট {% endif %} কোনো নেস্টেড স্ট্রাকচারের ভেতরে ইনডেন্ট করা থাকে, তবে ভিজ্যুয়াল অ্যালাইনমেন্ট আপনাকে অমিলটি বুঝতে সাহায্য করবে। যদি আপনার HTML এবং Liquid ট্যাগ একই ইনডেন্টেশন স্কিম অনুসরণ করে, তবে ভুল গভীরতায় থাকা ট্যাগটি আপনার চোখে সহজেই পড়বে।
আসল পাঠ্যক্রম
উইকেন্ড চ্যালেঞ্জগুলো গুরুত্বপূর্ণ কারণ এগুলো ঠিক সেই পরিস্থিতি তৈরি করে যে পরিস্থিতিতে আপনি আসলে কাজ করেন। কোনো ম্যানেজার দেখছেন না। কোনো ডেডলাইন চাপিয়ে দেওয়া নেই। আপনি দক্ষতা বা আনন্দের জন্য কোড করছেন, আর ঠিক তখনই একটি অতি ক্ষুদ্র ভুল আপনাকে থামিয়ে দিচ্ছে। সেই মুহূর্তটিই হলো আসল শিক্ষা। ডিবাগিং সম্পর্কে পড়ে আপনি ডিবাগিং শিখতে পারেন না। আপনি শিখেন একটি ত্রুটিপূর্ণ বিল্ডের দিকে তাকিয়ে থেকে, যখন আপনার বাইরে ঘুরতে ইচ্ছে করছিল; নিজেকে বাধ্য করে একটি এরর মেসেজকে সমালোচনা হিসেবে না দেখে ডেটা হিসেবে গ্রহণ করার মাধ্যমে আপনি শিখেন।
এই ভুলগুলো ঠিক করা শিখুন কারণ এগুলো কখনোই পুরোপুরি হারিয়ে যায় না। ক্যারিয়ারের দশ বছর পরেও, শুক্রবার রাতে কোনো ডিপ্লয়মেন্টের সময় আপনি হয়তো কোনো ক্লোজিং ট্যাগ ভুলে যাবেন। একজন জুনিয়র এবং একজন সিনিয়র ডেভেলপারের মধ্যে পার্থক্য ভুল না করা নয়, বরং ভুল কাটিয়ে ওঠার গতি। সিনিয়র সিনট্যাক্স এররটি দেখেন, প্যাটার্নটি চিনতে পারেন, সম্ভাব্য কারণগুলো পরীক্ষা করেন এবং এগিয়ে যান। জুনিয়র ভাবতে থাকেন যে পুরো টুলচেইনটিই কি ভেঙে গেছে? বারবার অনুশীলনের মাধ্যমেই এই রিফ্লেক্স তৈরি হয়।
কমিউনিটির অংশটি এই প্রক্রিয়াকে ত্বরান্বিত করে। যখন একাধিক মানুষ একটি উইকেন্ডে একই ত্রুটিপূর্ণ টেমপ্লেট নিয়ে কাজ করে, তখন এমন কিছু প্যাটার্ন বেরিয়ে আসে যা কোনো একক ডেভেলপার একা দেখতে পায় না। কেউ লক্ষ্য করেন যে এররটি শুধুমাত্র নেস্টেড for লুপের ভেতরেই ঘটে। অন্য কেউ একটি শেল স্ক্রিপ্ট শেয়ার করেন যা সাধারণ Liquid ট্যাগ অমিলগুলো খুঁজে বের (grep) করতে পারে। জ্ঞান সঞ্চয় করে নয়, বরং বিনিময়ের মাধ্যমে বৃদ্ধি পায়। আপনি নির্দিষ্ট চ্যালেঞ্জটির বিস্তারিত পড়তে পারেন এবং অন্যরা এটি কীভাবে সমাধান করেছে তা দেখতে পারেন এই Dev.to পোস্টে। আপনি যদি একই সমস্যায় পড়া মানুষের সাথে অভিজ্ঞতা বিনিময় করতে চান, তবে Telegram-এ একটি ঐচ্ছিক লার্নিং কমিউনিটি রয়েছে যেখানে এই আলোচনাগুলো উইকেন্ডের পরেও চলতে থাকে।
মূল শিক্ষা
সিনট্যাক্স এররগুলোকে আপনার আসল কাজের বাধা হিসেবে নেবেন না। এগুলোও কাজেরই একটি মৌলিক অংশ। এই উইকেন্ডের বিল্ডটি যে Liquid ট্যাগটি ভেঙেছিল, তা আসলে টেমপ্লেট ইঞ্জিন নিয়ে ছিল না। এটি ছিল আপনার মস্তিষ্ক যখন অনুমান করতে চায়, তখন নিখুঁতভাবে পড়ার জন্য নিজেকে প্রশিক্ষণ দেওয়া। গত সপ্তাহে লেখা একটি ফাইল খুলুন। আপনি যে ট্যাগগুলো ওপেন করেছিলেন সেগুলো খুঁজুন। নিশ্চিত করুন যে প্রতিটি ট্যাগ সঠিকভাবে বন্ধ করা হয়েছে। আপনার লুপগুলো শেষ করুন। কন্ডিশনালগুলো ঠিক করুন। তারপর আবার বিল্ড করা শুরু করুন, প্রতিটি সঠিক ক্যারেক্টারের মাধ্যমে।
