ডাটাবেস কনস্ট্রেইন্ট ত্রুটি UX: ব্যর্থতাকে স্পষ্ট বার্তায় বদলান
ডাটাবেস কনস্ট্রেইন্ট ত্রুটিগুলো কীভাবে স্পষ্ট ফিল্ড-বার্তায় রূপান্তর করা যায় জানুন — ইউনিক, ফরেন কি ও NOT NULL ব্যর্থতাগুলোকে ব্যবহারকারী দ্রুত ঠিক করতে পারার মতো দেখানো।

কেন কনস্ট্রেইন্ট ব্যর্থতা ব্যবহারকারীদের জন্য খারাপ মনে হয়
যখন কেউ Save চাপে, তারা দুটি ফলাফলের একটিই আশা করে: এটি কাজ করেছে, অথবা তারা দ্রুত যা ভুল হয়েছে তা ঠিক করতে পারবে। প্রায়ই তারা পায় একটি ধোঁয়াশাপূর্ণ ব্যানার যেমন “Request failed” বা “Something went wrong.” ফর্ম অপরিবর্তিত থাকে, কিছু হাইলাইট হয় না, এবং ব্যবহারকারী অনুমান করে ফেলে।
এই ফাঁককেই কনস্ট্রেইন্ট ত্রুটি UX গুরুত্বপূর্ণ করে তোলে। ডাটাবেস এমন নিয়ম আরোপ করে যা ব্যবহারকারী দেখেনি: “এই মানটি অনন্য হতে হবে,” “এই রেকর্ডটি কোনো বিদ্যমান আইটেমকে রেফার করতে হবে,” “এই ফিল্ড খালি থাকা চলবে না।” যদি অ্যাপ সেই নিয়মগুলোকে অস্পষ্ট ত্রুটির আড়ালে লুকায়, মানুষ নিজেদের বোঝাতে পারে না এমন একটি সমস্যার জন্য দোষী মনে করে।
সাধারণ ত্রুটি আস্থা ভাঙে। ব্যবহারকারীরা ধরে নেয় অ্যাপ অস্থির, তাই তারা পুনরায় চেষ্টা করে, রিফ্রেশ করে, বা কাজটি ত্যাগ করে। একটি ওয়ার্ক সেটিং-এ তারা সাপোর্ট-এ স্ক্রিনশট পাঠায় যা কাজে লাগে না, কারণ স্ক্রিনশটে কোন উপকারী বিস্তারিত থাকে না।
একটি সাধারণ উদাহরণ: কেউ একটি কাস্টমার রেকর্ড তৈরি করে এবং পায় “Save failed.” তারা একই ইমেইল দিয়ে আবার চেষ্টা করে। আবার ব্যর্থ হয়। এখন তারা ভাবতে শুরু করে: সিস্টেম কি ডুপ্লিকেট করছে, ডেটা হারাচ্ছে, না উভয়ই?
ডাটাবেস প্রায়ই চূড়ান্ত সত্যের উৎস — এমনকি যখন আপনি UI-তে ভ্যালিডেশন করেন। এটি সর্বশেষ অবস্থা দেখে, অন্য ব্যবহারকারীদের পরিবর্তন, ব্যাকগ্রাউন্ড জব এবং ইন্টিগ্রেশনগুলো সহ। তাই কনস্ট্রেইন্ট ব্যর্থতা ঘটবে, এবং সেটা স্বাভাবিক।
একটি ভাল ফলাফল সহজ: ডাটাবেস নিয়মকে এমন একটি বার্তায় বদলান যা একটি নির্দিষ্ট ফিল্ড ও পরবর্তী ধাপ নির্দেশ করে। উদাহরণ:
- “Email ইতিমধ্যেই ব্যবহার হচ্ছে। অন্য ইমেইল ব্যবহার করুন অথবা সাইন ইন করুন.”
- “একটি বৈধ অ্যাকাউন্ট নির্বাচন করুন। নির্বাচিত অ্যাকাউন্টটি আর নেই.”
- “ফোন নম্বর আবশ্যক।”
বাকি প্রবন্ধটি সেই অনুবাদ করার উপায় নিয়ে — যাতে ব্যর্থতাগুলো দ্রুত পুনরুদ্ধারে বদলে যায়, আপনি স্ট্যাক হ্যান্ড-কোড করুন বা AppMaster-এর মতো টুল ব্যবহার করুন।
কোন কনস্ট্রেইন্ট ধরনের সাথে দেখা হবে (এবং এগুলো কী বোঝায়)
অধিকাংশ “request failed” মুহূর্তগুলো একটি ছোট সেট ডাটাবেস নিয়ম থেকে আসে। যদি আপনি নিয়মটার নাম জানেন, সাধারণত সেটিকে একটি স্পষ্ট বার্তায় রূপান্তর করা যায় এবং ঠিক ফিল্ডে দেখানো যায়।
নিচে সাধারণ কনস্ট্রেইন্ট ধরনগুলো সাধারণ ভাষায়:
- Unique constraint: একটি মান অনন্য হতে হবে। সাধারণ উদাহরণ: ইমেইল, ইউজারনেম, ইনভয়েস নম্বর, বা কোনো এক্সটার্নাল ID। ব্যর্থ হলে ব্যবহারকারী “ভুল” করেছে না—তারা বিদ্যমান ডেটার সাথে সংঘর্ষে পড়েছে।
- Foreign key constraint: একটি রেকর্ড আরেকটি রেকর্ডকে নির্দেশ করে যা অবশ্যই থাকা দরকার (যেমন
order.customer_id). এটি ব্যর্থ হয় যখন রেফারেন্স করা আইটেমটি মুছে ফেলা হয়েছে, কখনোই বিদ্যমান ছিল না, বা UI ভুল ID পাঠিয়েছে। - NOT NULL constraint: একটি আবশ্যক মান ডাটাবেস স্তরে অনুপস্থিত। এটা ঘটতে পারে যদিও ফর্ম সমাপ্ত মনে হয় (উদাহরণ: UI কোন ফিল্ড পাঠায়নি, বা API সেটি ওভাররাইট করেছে)।
- Check constraint: একটি মান অনুমোদিত নিয়মের বাইরে, যেমন “quantity must be > 0,” “status must be one of these values,” বা “discount must be between 0 and 100.”
কঠিন অংশ হলো একই বাস্তব-জগতের সমস্যা ডাটাবেস এবং টুলিং অনুযায়ী ভিন্ন রূপে প্রদর্শিত হতে পারে। Postgres constraint নাম দিতে পারে (সেটা সাহায্য করে), আরেকদিকে ORM এটি একটি generic exception-এ আবৃত করে (কম সাহায্য করে)। এমনকি একই unique constraint “duplicate key,” “unique violation,” বা ভেন্ডর-নির্দিষ্ট ত্রুটি কোড হিসেবে দেখাতে পারে।
একটি ব্যবহারিক উদাহরণ: কেউ একজন অ্যাডমিন প্যানেলে একটি কাস্টমার সম্পাদনা করে, Save চাপলে ব্যর্থ হয়। যদি API UI-কে বলে দিতে পারে যে এটি email-এ একটি unique constraint ছিল, আপনি Email ফিল্ডের নীচে “This email is already used” দেখাতে পারবেন পরিবর্তে অস্পষ্ট টোস্টের।
প্রতিটি কনস্ট্রেইন্ট টাইপকে এমন একটি ইঙ্গিত হিসেবে দেখুন যা ব্যবহারকারীকে পরবর্তী করণীয় বলে: অন্য মান নির্বাচন করা, বিদ্যমান সম্পর্কিত রেকর্ড বেছে নেওয়া, বা অনুপস্থিত আবশ্যক ফিল্ড পূরণ করা।
একটি ভালো ফিল্ড-লেভেল বার্তায় কী থাকা উচিত
ডাটাবেস কনস্ট্রেইন্ট ব্যর্থতা একটি প্রযুক্তিগত ঘটনা, কিন্তু অভিজ্ঞতাটি সাধারণ নির্দেশনার মত অনুভূত হওয়া উচিত। ভালো কনস্ট্রেইন্ট ত্রুটি UX “কিছু ভাঙলো”কে “এটাই ঠিক করতে হবে” তে বদলে দেয়, ব্যবহারকারীকে অনুমান করাতে না করে।
সরল ভাষা ব্যবহার করুন। “unique index” বা “foreign key” এর মতো ডাটাবেস শব্দগুলো মানুষের বলার মতো কথায় বদলান। “That email is already in use” অনেক বেশি উপকারী “duplicate key value violates unique constraint” এর চেয়ে।
বার্তাটি যেখানে কাজ হচ্ছে সেখানে রাখুন। যদি ত্রুটি স্পষ্টভাবে একটি ইনপুটের সাথে যুক্ত, তাহলে সেই ফিল্ডের কাছে অট্যাচ করুন যাতে ব্যবহারকারী তা তৎক্ষণাৎ ঠিক করতে পারে। যদি এটি সম্পূর্ণ অ্যাকশনের ব্যাপারে (যেমন “আপনি এটা মুছতে পারবেন না কারণ এটি অন্যকিছুতে ব্যবহৃত”), তাহলে Save বোতামের কাছে ফর্ম-লেভেলে দেখান এবং পরবর্তী ধাপ স্পষ্ট বলুন।
নির্দিষ্ট হওয়া ভদ্রতার চেয়ে ভালো। একটি সহায়ক বার্তা দুইটি প্রশ্নের উত্তর দেয়: কী পরিবর্তন করা দরকার, এবং কেন এটি প্রত্যাখ্যান করা হলো। “Choose a different username” ভালো “Invalid username” থেকে। “Select a customer before saving” ভালো “Missing data” থেকে।
সংবেদনশীল বিবরণ নিয়ে সাবধান থাকুন। কিছুক্ষেত্রে সবচেয়ে “সহায়ক” বার্তা তথ্য ফাঁস করে দেয়। লগইন বা পাসওয়ার্ড রিসেট পর্দায় “No account exists for this email” বলা আক্রমণকারীদের সাহায্য করতে পারে। সেগুলোতে নিরাপদ বার্তা ব্যবহার করুন যেমন “If an account matches this email, you’ll get a message soon.”
একাধিক সমস্যার জন্যও পরিকল্পনা করুন। একটি সেভ একাধিক কনস্ট্রেইন্টে ব্যর্থ হতে পারে। আপনার UI একাধিক ফিল্ড বার্তা একসাথে দেখাতে সক্ষম হওয়া উচিত, স্ক্রিনটি ওভারওয়েল্ম না করে।
একটি শক্ত ফিল্ড-লেভেল বার্তা সরল শব্দ ব্যবহার করে, সঠিক ফিল্ডকে নির্দেশ করে (অথবা স্পষ্টভাবে ফর্ম-লেভেল), কী পরিবর্তন করতে হবে বলে, ব্যক্তিগত তথ্য প্রকাশ করে না, এবং একসাথে একাধিক ত্রুটিকে সমর্থন করে।
API এবং UI এর মধ্যে একটি ত্রুটি কনট্রাক্ট ডিজাইন করুন
ভালো UX একটি চুক্তি থেকে শুরু হয়: কিছু ভুল হলে API UI-কে ঠিক কি ঘটেছে বলে, এবং UI প্রতিবার একইভাবে তা দেখায়। এই চুক্তি না থাকলে আপনি আবার ঐ অস্পষ্ট টোস্টের দিকে ফিরে যাচ্ছেন, যা কাউকে কাজে লাগায় না।
একটি বাস্তবসম্মত ত্রুটি আকৃতি ছোট কিন্তু স্পষ্ট হওয়া উচিত। এতে একটি স্থিতিশীল ত্রুটি কোড, ফিল্ড (যদি এটি একটি ইনপুটের সাথে মানানসই হয়), একটি মানব-ঠাট্টাভঙ্গি বার্তা, এবং লগিংয়ের জন্য ঐচ্ছিক বিস্তারিত থাকা উচিত।
{
"error": {
"code": "UNIQUE_VIOLATION",
"field": "email",
"message": "That email is already in use.",
"details": {
"constraint": "users_email_key",
"table": "users"
}
}
}
কীটি হলো স্থিতিশীলতা। কাঁচা ডাটাবেস টেক্সট ব্যবহারকারীর কাছে প্রকাশ করবেন না, এবং UI-কে Postgres এর ত্রুটি স্ট্রিং পার্স করাতে দেবেন না। কোডগুলো প্ল্যাটফর্ম জুড়ে (ওয়েব, iOS, Android) এবং এন্ডপয়েন্ট জুড়ে ধারাবাহিক হওয়া উচিত।
আগে থেকে নির্ধারণ করুন কিভাবে আপনি ফিল্ড ত্রুটিগুলো বনাম ফর্ম-লেভেল ত্রুটিগুলো প্রদর্শন করবেন। একটি ফিল্ড ত্রুটি মানে একটি ইনপুট ব্লক হচ্ছে (field সেট করুন, ইনপুটের নীচে বার্তা দেখান)। একটি ফর্ম-লেভেল ত্রুটি মানে অ্যাকশন সম্পন্ন করা যাবে না যদিও ফিল্ডগুলো ঠিকমতো দেখায় (field ফাঁকা রাখুন, Save-র কাছে বার্তা দেখান)। যদি একাধিক ফিল্ড একসাথে ব্যর্থ হতে পারে, একটি ত্রুটির অ্যারে ফেরত দিন, প্রতিটির নিজস্ব field এবং code সহ।
রেন্ডারিং ধারাবাহিক রাখতে UI নিয়মগুলো নীরস ও পূর্বানুমানযোগ্য রাখুন: প্রথম ত্রুটিটি সংক্ষিপ্ত সারাংশ হিসেবে শীর্ষে দেখান এবং ইনলাইন ফিল্ডের পাশে দেখান, বার্তাগুলো সংক্ষিপ্ত ও কার্যকরী রাখুন, বিভিন্ন ফ্লোতে একই শব্দবলী পুনরায় ব্যবহার করুন (signup, profile edit, admin screens), এবং details লগ করুন যখন শুধুই message দেখান।
আপনি যদি AppMaster-এ তৈরি করেন, এই চুক্তিটিকে যেকোনো অন্য API আউটপুটের মতো বিবেচনা করুন। আপনার ব্যাকএন্ড স্ট্রাকচার্ড আকারে রেসপন্স দিতে পারবে এবং জেনারেটেড ওয়েব (Vue3) ও মোবাইল অ্যাপগুলো একটি শেয়ার করা প্যাটার্ন দিয়ে এটি রেন্ডার করতে পারবে, যাতে প্রতিটি কনস্ট্রেইন্ট ব্যর্থতা নির্দেশনার মত লাগে, ক্র্যাশের মত না।
ধাপে ধাপে: DB ত্রুটিগুলোকে ফিল্ড বার্তায় অনুবাদ করুন
ভালো কনস্ট্রেইন্ট ত্রুটি UX শুরু হয় ডাটাবেসকে ইয়ার্নিং বিচারক হিসেবে দেখার মাধ্যমে, প্রথম লাইন হিসেবে নয়। ব্যবহারকারী কখনই কাঁচা SQL টেক্সট, স্ট্যাক ট্রেস, বা অস্পষ্ট “request failed” দেখবে না। তাদের বলা উচিত কোন ফিল্ডে মনোযোগ দিতে হবে এবং পরবর্তী ধাপ কী।
একটি ব্যবহারিক ফ্লো যা বেশিরভাগ স্ট্যাকেই কাজ করে:
- নির্ধারণ করুন কোথায় ত্রুটি ধরা হবে। একটি জায়গা নির্ধারণ করুন যেখানে ডাটাবেস ত্রুটিগুলো API রেসপন্সে রূপান্তরিত হবে (প্রায়শই রেপোজিটরি/DAO লেয়ার বা একটি গ্লোবাল ত্রুটি হ্যান্ডলার)। এটি “কখনো ইনলাইন, কখনো টোস্ট” বিশৃঙ্খলা রোধ করে।
- ব্যর্থতাটি শ্রেণীবদ্ধ করুন। যখন একটি write ব্যর্থ হয়, ক্লাস নির্ণয় করুন: unique constraint, foreign key, NOT NULL, বা check constraint। সম্ভব হলে ড্রাইভার ত্রুটি কোড ব্যবহার করুন। মানুষের টেক্সট পার্স করা এড়ান যদি না আপনার কাছে অন্য উপায় না থাকে।
- কনস্ট্রেইন্ট নামকে ফর্ম ফিল্ডে ম্যাপ করুন। কনস্ট্রেইন্টগুলো দারুণ আইডেন্টিফায়ার, কিন্তু UI-কে ফিল্ড কী দরকার। একটি সাধারণ লুকআপ রাখুন যেমন
users_email_key -> emailবাorders_customer_id_fkey -> customerId। এটিকে সেই কোডের কাছে রাখুন যা স্কিমার মালিক। - নিরাপদ বার্তা তৈরি করুন। কাঁচা DB বার্তা না করে ক্লাস অনুযায়ী সংক্ষিপ্ত, ব্যবহারকারী-বান্ধব টেক্সট তৈরি করুন। Unique -> “This value is already in use.” FK -> “Choose an existing customer.” NOT NULL -> “This field is required.” Check -> “Value is outside the allowed range.”
- স্ট্রাকচার্ড ত্রুটি ফেরত দিন এবং ইনলাইন রেন্ডার করুন। একটি ধারাবাহিক পে-লোড পাঠান (উদাহরণ:
[{ field, code, message }])। UI-তে বার্তাগুলোকে ফিল্ডের সাথে সংযুক্ত করুন, প্রথম ব্যর্থ ফিল্ডে স্ক্রোল ও ফোকাস দিন, এবং যদি দরকার হয় গ্লোবাল ব্যানারকে কেবল সারাংশে রাখুন।
আপনি যদি AppMaster-এ তৈরি করেন, একই ধারণা প্রয়োগ করুন: ব্যাকএন্ডের এক জায়গায় ডাটাবেস ত্রুটি ধরুন, এটিকে একটি পূর্বানুমানযোগ্য ফিল্ড-তরকারী ফরম্যাটে অনুবাদ করুন, তারপর ওয়েব বা মোবাইল UI-তে ইনপুটের পাশে দেখান। এটি অভিজ্ঞতাকে ধারাবাহিক রাখে যদিও আপনার ডেটা মডেল পরিবর্তিত হয়।
বাস্তবসম্মত উদাহরণ: তিনটি ব্যর্থ সেভ, তিনটি সহায়ক আউটকাম
এই ব্যর্থতাগুলো প্রায়ই একটি জেনেরিক টোস্টে ভেঙে ফেলা হয়। প্রতিটিরই আলাদা বার্তা দরকার, যদিও সবগুলোই ডাটাবেস থেকে আসে।
1) সাইনআপ: ইমেইল ইতিমধ্যেই ব্যবহৃত (unique constraint)
র ডিফল্ট ব্যর্থতা (লগে যা দেখা যেতে পারে): duplicate key value violates unique constraint "users_email_key"
ব্যবহারকারীকে যা দেখানো উচিত: “That email is already registered. Try signing in, or use a different email.”
বার্তাটি Email ফিল্ডের পাশে রাখুন এবং ফর্মটি পূর্ণ রেখুন। যদি সম্ভব হয়, একটি সেকেন্ডারি অ্যাকশন অফার করুন যেমন “Sign in,” যাতে তারা কি ঘটেছে অনুমান না করে।
2) অর্ডার তৈরি: গ্রাহক অনুপস্থিত (foreign key)
র ডিফল্ট ব্যর্থতা: insert or update on table "orders" violates foreign key constraint "orders_customer_id_fkey"
ব্যবহারকারীকে যা দেখানো উচিত: “Choose a customer to place this order.”
এটি ব্যবহারকারীর কাছে একটি “ত্রুটি” হিসেবে না থেকে অনুপস্থিত প্রসঙ্গের মত লাগে। Customer সিলেক্টর হাইলাইট করুন, তাদের যে লাইন আইটেমগুলো যোগ করেছেন সেগুলো রাখুন, এবং যদি গ্রাহকটি অন্য ট্যাবে থেকে মুছে ফেলা হয়েছিল বলে মনে হয় তাহলে স্পষ্টভাবে বলুন: “That customer no longer exists. Pick a different one.”
3) প্রোফাইল আপডেট: আবশ্যক ফিল্ড অনুপস্থিত (NOT NULL)
র ডিফল্ট ব্যর্থতা: null value in column "last_name" violates not-null constraint
ব্যবহারকারীকে যা দেখানো উচিত: “Last name is required.”
এটাই ভালো কনস্ট্রেইন্ট হ্যান্ডলিং: সাধারণ ফর্ম ফিডব্যাক, সিস্টেম ফেইল নয়।
সাপোর্টের জন্য প্রযুক্তিগত বিবরণ ফাঁস না করে, পুরো ত্রুটিটি লগে রাখুন (বা একটি অভ্যন্তরীণ ত্রুটি প্যানেলে): একটি request ID এবং user/session ID, কনস্ট্রেইন্ট নাম (যদি থাকে) এবং table/field, API পে-লোড (সেন্সিটিভ ফিল্ডগুলো মাস্ক করুন), টাইমস্ট্যাম্প ও এন্ডপয়েন্ট/অ্যাকশন, এবং ব্যবহারকারীর জন্য দেখানো বার্তাটি।
ফরেন কি ত্রুটি: ব্যবহারকারীকে কিভাবে পুনরুদ্ধার করাবেন
ফরেন কি ব্যর্থতাগুলো সাধারণত মানে ব্যবহারকারী এমন কিছু নির্বাচন করেছে যা আর নেই, আর গ্রহণযোগ্য নয়, বা বর্তমান নিয়মের সঙ্গে মিলছে না। লক্ষ্য কেবল ব্যর্থতা ব্যাখ্যা করা নয়, বরং তাদের একটি স্পষ্ট পরবর্তী পদক্ষেপ দেওয়া।
অধিকাংশ ক্ষেত্রে, একটি foreign key ত্রুটি একটি ফিল্ডের সাথে ম্যাপ হয়: সেই পিকার যেটি অন্য রেকর্ড রেফার করে (Customer, Project, Assignee)। বার্তাটি সেই জিনিসটির নাম বলুক যা ব্যবহারকারী চিনে, ডাটাবেস কনসেপ্ট নয়। অভ্যন্তরীণ ID বা টেবিল নাম এড়ান। “Customer no longer exists” ব্যবহারকারীকে কাজে লাগবে; “FK_orders_customer_id violated (customer_id=42)” হবে না।
একটি শক্ত রিকভারি প্যাটার্ন হলো ত্রুটিটিকে একটি স্টেল সিলেকশন হিসেবে আচরণ করা। ব্যবহারকারীকে সর্বশেষ তালিকা থেকে পুনরায় নির্বাচন করতে বলুন (ড্রপডাউন রিফ্রেশ করুন বা সার্চ পিকার খুলুন)। যদি রেকর্ডটি মুছে বা আর্কাইভ করা হয়ে থাকে, সেটা স্পষ্ট বলুন এবং তাদের সক্রিয় বিকল্পে গাইড করুন। যদি ব্যবহারকারীর কাছে অ্যাক্সেস না থাকে, বলুন “You no longer have permission to use this item,” এবং বিকল্প বেছে নিতে বা অ্যাডমিনের সঙ্গে যোগাযোগ করতে বলুন। যদি সম্পর্কিত রেকর্ড তৈরি করা সাধারণ পরবর্তী ধাপ হয়, “Create new customer” অফার করুন পুনরায় চেষ্টা চাপানোর বদলে।
মুছে ফেলা এবং আর্কাইভ করা রেকর্ডগুলো সাধারণ ফাঁদ। যদি আপনার UI ইনঅ্যাকটিভ আইটেমগুলো দেখাতে পারে, সেগুলোকে স্পষ্টভাবে (Archived) লেবেল করুন এবং সিলেকশন অবরুদ্ধ করুন। এটা ব্যর্থতা প্রতিরোধ করে, তবে অন্য ব্যবহারকারী ডেটা পরিবর্তন করলে তবুও হ্যান্ডল করে।
কখনও কখনও একটি foreign key ব্যর্থতা ফর্ম-লেভেল হওয়া উচিত, ফিল্ড-লেভেল নয়। সেটি তখন করুন যখন আপনি নির্ভরযোগ্যভাবে বলতে না পারেন কোন রেফারেন্স ত্রুটির কারণ, যখন একাধিক রেফারেন্স অবৈধ, বা যখন বাস্তব সমস্যা হলো পারমিশন স্তরের জটিলতা।
NOT NULL এবং ভ্যালিডেশন: ত্রুটি প্রতিরোধ করুন, তবু হ্যান্ডল করুন
NOT NULL ব্যর্থতাগুলো প্রতিরোধ করা সবচেয়ে সহজ এবং স্লিপ হলে সবচেয়ে বিরক্তিকর। যদি কেউ.Required ফিল্ড ফাঁকা রেখে “request failed” দেখে, ডাটাবেস UI-এর কাজ করছে। ভালো কনস্ট্রেইন্ট UX মানে UI স্পষ্ট কেসগুলো ব্লক করে, এবং API তবুও পরিষ্কার ফিল্ড-লেভেল ত্রুটি ফেরত দেয় যখন কিছু ফাঁকি খায়।
ফর্মে প্রাথমিক চেক দিয়ে শুরু করুন। আবশ্যক ফিল্ডগুলো ইনপুটের কাছে চিহ্নিত করুন, generic ব্যানারের বদলে। “Required for receipts” মত সংক্ষিপ্ত হিন্ট একটি লাল অ্যাস্টেরিস্ক থেকে বেশি সহায়ক। যদি কোনো ফিল্ড শর্তসাপেক্ষভাবে আবশ্যক হয় (উদাহরণ: “Company name” কেবল তখনই যখন “Account type = Business”), সেই নিয়মটিকে প্রাসঙ্গিক মুহূর্তে দৃশ্যমান করুন।
UI ভ্যালিডেশন পর্যাপ্ত নয়। ব্যবহারকারীরা পুরোনো অ্যাপ ভার্সন, ঝাপসা নেটওয়ার্ক রিট্রাই, বাল্ক ইম্পোর্ট বা অটোমেশন দিয়ে এড়িয়ে যেতে পারে। তাই API-তেও একই নিয়ম প্রতিচ্ছবি রাখুন যাতে আপনি এক রাউন্ড ট্রিপ নষ্ট না করেন এবং ডাটাবেসে ব্যর্থ হন।
শব্দচয়নে ধারাবাহিক থাকুন যেন ব্যবহারকারীরা শেখে প্রতিটি বার্তাটি কী মানে। অনুপস্থিত মানের জন্য “Required” ব্যবহার করুন। দৈর্ঘ্য সীমার জন্য “Too long (max 50 characters).” ফরম্যাট চেকের জন্য “Invalid format (use [email protected]).” টাইপ সমস্যার জন্য “Must be a number.”
পার্শিয়াল আপডেটগুলোতে NOT NULL জটিল হয়ে যায়। একটি PATCH যা একটি আবশ্যক ফিল্ড ছেড়ে দেয়া উচিত নয় ব্যর্থ হলে যদি বিদ্যমান মান ইতোমধ্যেই থাকে, তবে ব্যর্থ হওয়া উচিত যদি ক্লায়েন্ট স্পষ্টভাবে এটিকে null বা খালি মানে সেট করে। একবার এই নিয়ম নির্ধারণ করে নিন, ডকুমেন্ট করুন, এবং ধারাবাহিকভাবে এহার প্রয়োগ করুন।
একটি ব্যবহারিক উপায় হলো তিন স্তরে ভ্যালিডেশন: ক্লায়েন্ট ফর্ম নিয়ম, API রিকোয়েস্ট ভ্যালিডেশন, এবং একটি চূড়ান্ত সেফটি নেট যা ডাটাবেস NOT NULL ত্রুটি ধরলে সঠিক ফিল্ডে ম্যাপ করে।
সাধারণ ভুলগুলো যা আবার “request failed”-এ ফিরিয়ে দেয়
কনস্ট্রেইন্ট হ্যান্ডলিং নষ্ট করার দ্রুততম উপায় হলো সমস্ত কঠিন কাজ ডাটাবেসে করে রাখা, তারপর ফলাফল একটি জেনেরিক টোস্টে লুকিয়ে রাখা। ব্যবহারকারীরা কনস্ট্রেইন্ট ট্রিগার হওয়া নিয়ে যত্ন করে না — তারা জানতে চায় কি ঠিক করতে হবে, কোথায়, এবং তাদের ডেটা নিরাপদ কি না।
একটি সাধারণ ভুল হলো কাঁচা ডাটাবেস টেক্সট দেখানো। duplicate key value violates unique constraint মতো বার্তাগুলো ক্র্যাশের মতো লাগে, এমনকি যখন অ্যাপ পুনরুদ্ধার করতে পারে। এগুলো সাপোর্ট টিকিটও তৈরি করে কারণ ব্যবহারকারীরা ভয়ঙ্কর টেক্সট কপি করে পাঠায় বদলে একটি ফিল্ড ঠিক করার।
আরেকটি ফাঁদ হলো স্ট্রিং ম্যাচিং-এ নির্ভর করা। এটা কাজ করে যতক্ষণ না আপনি ড্রাইভার পরিবর্তন করেন, Postgres আপগ্রেড করে, বা কনস্ট্রেইন্ট নাম পরিবর্তন করেন। তখন আপনার “email already used” ম্যাপিং নীরবে থেমে যায়, এবং আপনি আবার “request failed”-এ ফিরে যাবেন। স্থিতিশীল ত্রুটি কোড পছন্দ করুন এবং UI-কে বোঝার জন্য ফিল্ড নাম অন্তর্ভুক্ত করুন।
স্কীমা পরিবর্তন ফিল্ড ম্যাপিং ভেঙে দেয় বেশি বার বার। email থেকে primary_email-এ rename হলে একটি স্পষ্ট বার্তা কোথায় দেখাবেন তা অনুপলব্ধ হয়ে যেতে পারে। ম্যাপিংকে একই migration-র সাথে রাখুন, এবং টেস্টে সংকেত দিন যদি একটি ফিল্ড কী অজানা হয়ে যায়।
একটি বড় UX কিলার হলো প্রতিটি কনস্ট্রেইন্ট ব্যর্থতাকে HTTP 500 করে ফেলা এবং কোন বডি না দেওয়া। এটি UI-কে বলে “এটি সার্ভারের ত্রুটি,” তাই ফিল্ড-হিন্ট দেখাতে পারে না। বেশিরভাগ কনস্ট্রেইন্ট ব্যর্থতা ব্যবহারকারী সংশোধনযোগ্য, তাই একটি ভ্যালিডেশন-স্টাইল রেসপন্স ফিরিয়ে দিন বিস্তারিত সহ।
কিছু নজর দেওয়ার প্যাটার্ন:
- ইউনিক ইমেইল বার্তা যা একটি অ্যাকাউন্ট আছে বলে নিশ্চিত করে (signup ফ্লোতে নিরপেক্ষ শব্দ ব্যবহার করুন)
- “একবারে একটি ত্রুটি” হ্যান্ডলিং এবং দ্বিতীয় ভাঙা ফিল্ড লুকিয়ে রাখা
- মাল্টি-স্টেপ ফর্মগুলো যেখানে ব্যাক/নেক্সট ক্লিকে ত্রুটিগুলো হারিয়ে যায়
- রিট্রাই যা স্টেল মান সাবমিট করে এবং সঠিক ফিল্ড বার্তাকে ওভাররাইট করে
- লগিং যা কনস্ট্রেইন্ট নাম বা ত্রুটি কোড বাদ দেয়, বাগ ট্রেস করা কঠিন করে
উদাহরণস্বরূপ, যদি একটি সাইন-আপ ফর্ম বলে “Email already exists,” আপনি অ্যাকাউন্ট অস্তিত্ব ফাঁস করতে পারেন। একটি নিরাপদ বার্তা হবে “Check your email or try signing in,” এবং তবুও ইমেইল ফিল্ডে ত্রুটি সংযুক্ত রাখা।
পাঠানোর আগে দ্রুত চেকলিস্ট
শিপ করার আগে ছোট-বড় বিবরণগুলো চেক করুন যা নির্ধারণ করে একটি কনস্ট্রেইন্ট ব্যর্থতা সহায়ক নUDGE হবে নাকি ডেডএন্ড।
API রেসপন্স: UI কি ঠিকভাবে কাজ করতে পারবে?
প্রতিটি ভ্যালিডেশন-স্টাইল ব্যর্থতা নিশ্চিত করুন যাতে UI একটি নির্দিষ্ট ইনপুট নির্দেশ করতে পারে। প্রতিটি ত্রুটির জন্য field, একটি স্থিতিশীল code, এবং একটি মানববোধগম্য message ফেরত দিন। সাধারণ ডাটাবেস কেসগুলো (unique, foreign key, NOT NULL, check) কভার করুন। প্রযুক্তিগত বিস্তারিত লগে রাখুন, ব্যবহারকারীকে না দেখান।
UI আচরণ: কি এটি ব্যবহারকারীকে পুনরুদ্ধারে সাহায্য করে?
একটি নিখুঁত বার্তাও খারাপ লাগে যদি ফর্ম ব্যবহারকারীর সাথে লেগে থাকে না। প্রথম ব্যর্থ ফিল্ডে ফোকাস করুন এবং প্রয়োজন হলে স্ক্রল করে দেখানো নিশ্চিত করুন। ব্যবহারকারী যা টাইপ করেছে তা সংরক্ষণ করুন (বিশেষ করে মাল্টি-ফিল্ড ত্রুটির পরে)। ফিল্ড-লেভেল ত্রুটি প্রথমে দেখান, সারাংশ তখনই দেখান যখন সেটা উপকারী।
লগিং এবং টেস্ট: আপনি কি রিগ্রেশন ধরেন?
কনস্ট্রেইন্ট হ্যান্ডলিং ঘনঘন নীরবে ভাঙে যখন স্কিমা বদলায়, তাই এটাকে রক্ষণাবেক্ষিত ফিচারের মত বিবেচনা করুন। DB ত্রুটি অভ্যন্তরীণভাবে লগ করুন (constraint name, table, operation, request ID), কিন্তু কখনো সরাসরি দেখাবেন না। প্রতিটি কনস্ট্রেইন্ট টাইপের জন্য অন্তত একটি টেস্ট যোগ করুন এবং নিশ্চিত করুন আপনার ম্যাপিং স্থিতিশীল থাকে এমনকি ডাটাবেসের আক্ষরিক ভাষা বদলেও।
পরবর্তী ধাপ: আপনার অ্যাপ জুড়ে এটি ধারাবাহিক করুন
অধিকাংশ টিম এক স্ক্রিন করে কনস্ট্রেইন্ট ত্রুটি ঠিক করে। এটা সাহায্য করে, কিন্তু ব্যবহারকারীরা ফাঁকগুলো খেংচে ধরেন: একটি ফর্ম স্পষ্ট বার্তা দেখায়, অন্যটি এখনও “request failed” বলে। ধারাবাহিকতা এটাকে প্যাচ থেকে প্যাটার্নে বদলে দেয়।
যেখানে কষ্ট হচ্ছে সেখানে শুরু করুন। এক সপ্তাহের লগ বা সাপোর্ট টিকিট তুলে নিয়ে কিছু কনস্ট্রেইন্ট চিহ্নিত করুন যা বারবার দেখা যায়। সেই “টপ অফেন্ডার্স” প্রথমে বন্ধুভাবাপন্ন, ফিল্ড-লেভেল বার্তা পাবে।
ত্রুটি অনুবাদকে একটি ছোট প্রডাক্ট ফিচার হিসেবে বিবেচনা করুন। পুরো অ্যাপে একটি শেয়ার্ড ম্যাপিং রাখুন: constraint name (অথবা code) -> field name -> message -> recovery hint. বার্তাগুলো সরল রাখুন, এবং হিন্টগুলো কার্যকরী রাখুন।
একটি হালকা-ওজন রোলআউট প্ল্যান যা ব্যস্ত প্রোডাক্ট সাইকেলে মানায়:
- ব্যবহারকারীরা সবচেয়ে বেশি যে 5টি কনস্ট্রেইন্ট মুখোমুখি হচ্ছে তা চিহ্নিত করে সেগুলি জন্য নির্দিষ্ট বার্তা লিখুন।
- একটি ম্যাপিং টেবিল যোগ করুন এবং এটিকে প্রতিটি এন্ডপয়েন্টে ব্যবহার করুন যা ডেটা সেভ করে।
- ফর্মগুলো কিভাবে ত্রুটি রেন্ডার করে তা স্ট্যান্ডার্ডাইজ করুন (একই স্থান, একই টোন, একই ফোকাস আচরণ)।
- একটি না-তেকনিক্যাল সহকর্মীর সঙ্গে বার্তাগুলো রিভিউ করুন এবং জিজ্ঞেস করুন: “এখন তুমি কি করবে?”
- প্রতিটি ফর্মের জন্য একটি টেস্ট যোগ করুন যা সঠিক ফিল্ড হাইলাইট করে এবং বার্তাটি পাঠযোগ্য কিনা পরীক্ষা করে।
আপনি যদি প্রতিটি স্ক্রিন হাত দিয়ে লেখার বদলে এটা করতেই চান, AppMaster (appmaster.io) ব্যাকএন্ড API এবং জেনারেটেড ওয়েব এবং নেটিভ মোবাইল অ্যাপ সমর্থন করে। এটি একই স্ট্রাকচার্ড ত্রুটি ফরম্যাট ক্লায়েন্ট জুড়ে পুনরায় ব্যবহার করা সহজ করে, যাতে ফিল্ড-লেভেল ফিডব্যাক আপনার ডেটা মডেল বদলেও ধারাবাহিক থাকে।
দলের জন্য একটি সংক্ষিপ্ত “ত্রুটি বার্তা স্টাইল” নোটও লেখুন: সহজ রাখুন — কোন শব্দগুলো এড়াতে (ডাটাবেস টার্ম), এবং প্রতিটি বার্তায় কি থাকা বাধ্যতামূলক (কি ঘটেছে, পরবর্তী কী করা উচিত)।
প্রশ্নোত্তর
এটাকে একটি সাধারণ ফর্ম ফিডব্যাক হিসেবে দেখান, সিস্টেম ক্র্যাশ হিসেবে নয়। ঠিক যে ফিল্ড বদলানো লাগবে তার কাছে একটি সংক্ষিপ্ত বার্তা দেখান, ব্যবহারকারীর ইনপুট অক্ষুণ্ণ রাখুন, এবং পরবর্তী পদক্ষেপ সাধারণ ভাষায় বলুন।
একটি ফিল্ড-লেভেল ত্রুটি একটি ইনপুটকে নির্দেশ করে এবং ব্যবহারকারীকে সেসব জায়গায়ই বলে কি ঠিক করতে হবে — উদাহরণ: “Email ইতিমধ্যেই ব্যবহার হচ্ছে।” একটি সার্বিক ত্রুটি ব্যবহারকারীকে অনুমান করতে বাধ্য করে, রিট্রাই করতে বা সাপোর্ট ঠিকানা পাঠাতে বাধ্য করে।
সম্ভব হলে ডাটাবেস ড্রাইভার থেকে স্থিতিশীল ত্রুটি কোড ব্যবহারে নির্ণয় করুন, তারপর সেগুলোকে ইউজার-ফেসিং টাইপে (unique, foreign key, required, range) ম্যাপ করুন। কাঁচা DB টেক্সট পার্স করা এড়ান কারণ সেটি ড্রাইভার/ভার্সন অনুযায়ী পরিবর্তিত হতে পারে।
ব্যাকএন্ডে একটি সহজ ম্যাপ রাখুন যা কনস্ট্রেইন্ট নামকে UI ফিল্ড কী-এর সাথে যুক্ত করে। উদাহরণ: unique constraint on email -> email ফিল্ড। এটি schema-যত্নের নিকটে রাখুন যাতে UI ঠিক ফিল্ড হাইলাইট করতে পারে।
ডিফল্ট বার্তা হতে পারে “এই মানটি ইতিমধ্যেই ব্যবহার হচ্ছে” এবং প্রাসঙ্গিক পরবর্তী পদক্ষেপ দিন, যেমন “অন্যটি ব্যবহার করুন” বা “Sign in”। সাইন-আপ বা পাসওয়ার্ড রিসেট ধারা হলে নিরপেক্ষ শব্দ ব্যবহার করুন যাতে অ্যাকাউন্ট অস্তিত্ব নিশ্চিত না হয়।
এটাকে একটি পুরোনো/অবৈধ সিলেকশন হিসেবে ব্যাখ্যা করুন যা ব্যবহারকারী চিনে: “That customer no longer exists. Choose another.” যদি পুনরুদ্ধারের জন্য সম্পর্কিত রেকর্ড তৈরি করাই উপযুক্ত, UI-তে সেই পথটি অফার করুন বদলি রিট্রাই চাপানোর বদলে।
UI-তে required ফিল্ডগুলো চিহ্নিত করুন এবং সাবমিটের আগে ভ্যালিডেশন করুন, কিন্তু ডাটাবেস স্তরের ব্যর্থতার জন্যও সেফটি নেট রাখুন। ঘটলে ফিল্ডে সরল “Required” বার্তা দেখান এবং বাকি ফর্ম অক্ষুণ্ণ রাখুন।
একটি ত্রুটির অ্যারে ফেরত দিন, প্রতিটি আইটেমে একটি ফিল্ড কী, স্থিতিশীল কোড, এবং সংক্ষিপ্ত বার্তা থাকুক যাতে UI এগুলো একসাথে দেখাতে পারে। ক্লায়েন্টে প্রথম ব্যর্থ ফিল্ডে ফোকাস করুন কিন্তু অন্যান্য বার্তাগুলোও দৃশ্যমান রাখুন।
একটি কনসিস্টেন্ট পেইলোড ব্যবহার করুন যা ব্যবহারকারীকে দেখানোর জন্য যা লাগে তা আলাদা করে এবং লগের জন্য যা লাগে তা রাখে—উদাহরণ: একটি ইউজার-বার্তা এবং অভ্যন্তরীণ বিস্তারিত (constraint name, request ID)। কাঁচা SQL ত্রুটি কখনোই সরাসরি দেখাবেন না এবং UI-কে DB স্ট্রিং পার্স করাতে দেবেন না।
অন্যান্য ক্লায়েন্টে একই আচরণ চাইলে ব্যাকএন্ডে অনুবাদ কেন্দ্রীকরণ করুন, একক পূর্বানুমানযোগ্য ত্রুটি আকার ফেরত দিন, এবং প্রতিটি ফর্মে একভাবেই রেন্ডার করুন। AppMaster ব্যবহার করলে এই কাঠামো ব্যাকএন্ড API এবং জেনারেটেড ওয়েব/মোবাইল UI-তে পুনরায় ব্যবহার করা সহজ হয়।


