NOT NULL, CHECK, UNIQUE, FOREIGN KEY সহ PostgreSQL বিধিনিষেধের উপর নির্ভর করে AI-জেনারেটেড অ্যাপগুলো আরও নিরাপদভাবে শিপ করুন।

AI-লিখিত কোড প্রায়ই ঠিকঠাক দেখায় কারণ সেটা হ্যাপি-পাথকে হ্যান্ডেল করে। বাস্তব অ্যাপগুলো মাঝামাঝি অংশে ব্যর্থ হয়: একটি ফর্ম null না দিয়ে খালি স্ট্রিং পাঠায়, একটি ব্যাকগ্রাউন্ড জব রিট্রাই করে একই রেকর্ড দু'বার তৈরি করে, বা একটি ডিলিট প্যারেন্ট সারি মুছে ফেলে এবং চাইল্ডদের পিছনে ফেলে দেয়। এগুলো অদ্ভুত বাগ নয়। এগুলো দেখায় শূন্য প্রয়োজনীয় ক্ষেত্র, ডুপ্লিকেট "ইউনিক" মান, এবং অনাথ সারি যা কিছুই নির্দেশ করে না।
এসব কারণেই কোড রিভিউ ও বেসিক টেস্টগুলো স্লিপ করে দেয়: রিভিউয়াররা উদ্দেশ্য পড়ে, সব এজ-কেস পড়ে না। টেস্টগুলো সাধারণত কয়েকটি টিপিক্যাল উদাহরণ কভার করে, সপ্তাহব্যাপী ব্যবহারকারীর আচরণ, CSV থেকে ইম্পোর্ট, নড়বড়ে নেটওয়ার্ক রিট্রাই, বা সমান্তরাল অনুরোধগুলো কাভার করে না। যদি একটি অ্যাসিস্ট্যান্ট কোড জেনারেট করে, তখন সেটা ছোট কিন্তু গুরুত্বপূর্ণ চেকগুলো মিস করতে পারে—যেমন হোয়াইটস্পেস ট্রিম করা, রেঞ্জ ভ্যালিডেশন, বা রেস কন্ডিশন থেকে রক্ষা।
"প্রথমে বিধিনিষেধ, পরে কোড" মানে আপনি অননুমেয়নীয় নিয়মগুলো ডাটাবেসে রাখেন যাতে খারাপ ডেটা যেখানে-ই চেষ্টা করুক এটা সংরক্ষণ করা না যায়। আপনার অ্যাপ এখনও ইনপুট ভ্যালিডেশন করবে যাতে ভালো ত্রুটি বার্তা পাওয়া যায়, কিন্তু ডাটাবেসই শেষ সত্যটি জোরদার করবে। এখানে PostgreSQL বিধিনিষেধগুলো উজ্জ্বল: এগুলো আপনাকে ভুলের পুরো শ্রেণি থেকে রক্ষা করে।
একটি দ্রুত উদাহরণ: একটি ছোট CRM ভাবুন। একটি AI-জেনারেটেড ইম্পোর্ট স্ক্রিপ্ট কন্টাক্ট তৈরি করছে। এক সারির ইমেল "" (খালি), দুই সারি একই ইমেল বিভিন্ন কেসিং-এ পুনরাবৃত্তি করে, এবং একটি কন্টাক্ট একটি account_id রেফার করে যা আর নেই কারণ অন্য প্রসেসে অ্যাকাউন্টটি মুছে ফেলা হয়েছে। বিধিনিষেধ না থাকলে, সবগুলো প্রোডাকশনে ল্যান্ড করে পরে রিপোর্ট ভেঙে দিতে পারে।
ঠিক ডাটাবেস নিয়ম থাকলে সেই লেখাগুলো অবিলম্বে ব্যর্থ হয়, উত্সের কাছে কাছাকাছি। প্রয়োজনীয় ক্ষেত্রগুলো অনুপস্থিত হতে দেয়া হয় না, রিট্রাইয়ের সময় ডুপ্লিকেট ঢুকে পড়ে না, সম্পর্ক মুছে যাওয়া বা অস্থিত রেকর্ডকে পয়েন্ট করে না, এবং মানগুলো অনুমোদিত সীমার বাইরে পড়ে না।
বিধিনিষেধ সব বাগ আটকায় না। এগুলো বিভ্রান্তিকর UI, ভুল ডিসকাউন্ট গণনা, বা ধীর কুয়েরি ঠিক করবে না। কিন্তু এগুলো খারাপ ডেটা নীরবে জমে যাওয়া বন্ধ করে দেয়—এটাই প্রায়ই যেখানে "AI-জেনারেটেড এজ-কেস বাগ" ব্যয়বহুল হয়ে ওঠে।
আপনার অ্যাপ সাধারণত এক কোডবেস থেকে এক ব্যবহারকারী নয়। একটি সাধারণ প্রোডাক্টে থাকে ওয়েব UI, মোবাইল অ্যাপ, অ্যাডমিন স্ক্রিন, ব্যাকগ্রাউন্ড জব, CSV থেকে ইম্পোর্ট, এবং মাঝে মাঝে তৃতীয় পক্ষের ইন্টিগ্রেশন। প্রতিটি পথই ডেটা তৈরি বা পরিবর্তন করতে পারে। যদি প্রতিটি পথকে একই নিয়ম মনে রাখতে হয়, একটি ভুলেই যাবে।
ডাটাবেস একমাত্র স্থান যেখানে সব পথ শেয়ার করে। যখন আপনি এটাকে চূড়ান্ত গেটকিপার মনে করেন, নিয়মগুলো স্বয়ংক্রিয়ভাবে সবকিছুর ওপর প্রযোজ্য হয়। PostgreSQL বিধিনিষেধ "আমরা মনে করি এটা সবসময় সত্য" থেকে রূপান্তর করে „এটি অবশ্যই সত্য, না হলে লেখাটি ব্যর্থ হবে।“
AI-জেনারেটেড কোড এই কাজটাকে আরও গুরুত্বপূর্ণ করে তোলে। একটি মডেল React UI-তে ফর্ম ভ্যালিডেশন যোগ করতে পারে কিন্তু একটি ব্যাকগ্রাউন্ড জবে একটি কর্ণার কেস মিস করতে পারে। অথবা এটি হ্যাপি-পাথ ডেটা ভালোভাবে হ্যান্ডেল করে, তারপর বাস্তব গ্রাহক অপ্রত্যাশিত কিছু প্রবেশ করালে ভেঙে পড়ে। বিধিনিষেধ খারাপ ডেটা ডাটাবেসে ঢুকার মুহূর্তেই ধরবে, সপ্তাহ পর নয় যখন আপনি অদ্ভুত রিপোর্ট ডিবাগ করেন।
যখন আপনি বিধিনিষেধ স্কিপ করেন, খারাপ ডেটা প্রায়ই নীরব থাকে। সেভ সফল হয়, অ্যাপ এগিয়ে যায়, এবং পরে সমস্যা বলে উঠে সাপোর্ট টিকেট, বিলিং মিসম্যাচ, বা এমন একটি ড্যাশবোর্ড যা কেউ বিশ্বাস করে না। ক্লিনআপ ব্যয়বহুল হয় কারণ আপনি ইতিহাস ঠিক করছেন, এক অনুরোধ নয়।
খারাপ ডেটা সাধারণত দৈনন্দিন পরিস্থিতি থেকে ঢুকতে থাকে: একটি নতুন ক্লায়েন্ট অ্যাপ সংস্করণ একটি ফিল্ড খালি পাঠায় বদলে না পাঠানো, একটি রিট্রাই ডুপ্লিকেট তৈরি করে, একটি অ্যাডমিন এডিট UI চেকগুলো বাইপাস করে, একটি ইম্পোর্ট ফাইলে বিন্যাস অনিশ্চিত থাকে, অথবা দুই ব্যবহারকারী একই সময়ে সম্পর্কিত রেকর্ড আপডেট করে।
একটি দরকারি মানসিক মডেল: বাউন্ডারির কাছে কেবল বৈধ ডেটাই গ্রহণ করুন। বাস্তবে, সেই বাউন্ডারি ডাটাবেসটাও হওয়া উচিত, কারণ ডাটাবেস সব লেখাগুলো দেখে।
NOT NULL সবচেয়ে সাধারণ PostgreSQL বিধিনিষেধ, এবং এটি আশ্চর্যজনকভাবে বড় একটি বাগ শ্রেণি প্রতিরোধ করে। যদি একটি মান সারির জন্য থাকা উচিত, ডাটাবেসকে এটাই জোর করে দিন।
NOT NULL সাধারণত সঠিক হয় আইডেন্টিফায়ার, প্রয়োজনীয় নাম, এবং টাইমস্ট্যাম্পের জন্য। যদি আপনি এটি ছাড়া একটি বৈধ রেকর্ড তৈরি করতে না পারেন, এটাকে খালি রাখবেন না। একটি ছোট CRM-এ কোনও লিড যদি মালিক বা তৈরি সময় ছাড়া থাকে তাহলে সেটি একটি "আংশিক লিড" নয়—এটি ভাঙা ডেটা যা পরে অদ্ভুত আচরণ তৈরি করবে।
NULL বেশি করে ঢুকে পড়ে AI-জেনারেটেড কোডে কারণ সহজেই "বিকল্প" পথ তৈরি করা যায় ঘাবড়ে না দেখে। একটি ফর্ম ফিল্ড UI-তে ঐচ্ছিক থাকতে পারে, একটি API অনুপস্থিত কী গ্রহণ করতে পারে, এবং ক্রিয়েট ফাংশনের একটি শাখা মান অ্যাসাইন করা বাদ দিতে পারে। সবই কম্পাইল হয় এবং হ্যাপি-পাথ টেস্ট পাস করে। তারপর বাস্তব ব্যবহারকারী একটি খালি সেলে থাকা CSV ইম্পোর্ট করে, বা একটি মোবাইল ক্লায়েন্ট আলাদা পে-লোড পাঠায়, এবং NULL ডাটাবেসে ল্যান্ড করে।
ভালো একটি প্যাটার্ন হল NOT NULL sensible ডিফল্টের সঙ্গে মিলিয়ে ব্যবহার করা যেগুলো সিস্টেমই নিয়ন্ত্রণ করে:
created_at TIMESTAMP NOT NULL DEFAULT now()status TEXT NOT NULL DEFAULT 'new'is_active BOOLEAN NOT NULL DEFAULT trueডিফল্ট সবসময় লাভজনক নয়। ব্যবহারকারী-প্রদান করা ক্ষেত্র যেমন email বা company_name NOT NULL পূরণ করতে ডিফল্ট দেবেন না। একটি খালি স্ট্রিং NULL-এর চেয়ে "আরও বৈধ" নয়। এটি কেবল সমস্যাটিকে লুকিয়ে রাখে।
যখন আপনি অনিশ্চিত, তখন সিদ্ধান্ত নিন যে মানটি সত্যিই অজানা নাকি একটি আলাদা অবস্থা প্রকাশ করে। যদি "এখনো প্রদান করা হয়নি" মানে কিছু থাকে, তাহলে পুরো সিস্টেমে মানটি বজায় রাখতে আলাদা একটি স্ট্যাটাস কলাম বিবেচনা করুন। উদাহরণ: phone nullable রাখুন, কিন্তু phone_status রাখুন যেমন missing, requested, বা verified। এতে মানের অর্থ কোড জুড়ে সঙ্গত থাকবে।
একটি CHECK বিধিনিষেধ হলো আপনার টেবিলের একটি প্রতিশ্রুতি: প্রতিটি সারি প্রতিবার একটি নিয়ম পূরণ করতে হবে। এটি সহজপথে এজ-কেসগুলো প্রতিহত করার অন্যতম সহজ উপায়—যেগুলো কোডে ঠিক দেখতে ঠিক আছে কিন্তু বাস্তবে অর্থহীন।
CHECK বিধিনিষেধ সেই নিয়মগুলোর জন্য ভাল যেখানে কেবল একই সারির মানগুলোর উপর নির্ভর করে: সংখ্যাগত রেঞ্জ, অনুমোদিত মান, এবং কলামগুলোর মধ্যে সহজ সম্পর্ক।
-- 1) Totals should never be negative
ALTER TABLE invoices
ADD CONSTRAINT invoices_total_nonnegative
CHECK (total_cents >= 0);
-- 2) Enum-like allowed values without adding a custom type
ALTER TABLE tickets
ADD CONSTRAINT tickets_status_allowed
CHECK (status IN ('new', 'open', 'waiting', 'closed'));
-- 3) Date order rules
ALTER TABLE subscriptions
ADD CONSTRAINT subscriptions_date_order
CHECK (end_date IS NULL OR end_date >= start_date);
একটি ভাল CHECK এক নজরে পড়তে সুবিধা দেয়। এটাকে ডেটার ডকুমেন্টেশন মনে করুন। সংক্ষিপ্ত এক্সপ্রেশন, পরিষ্কার কনস্ট্রেইন্ট নাম, এবং প্রত্যাশিত প্যাটার্ন বেছে নিন।
CHECK সবকিছুর জন্য সঠিক নয়। যদি একটি নিয়ম অন্য সারি দেখার, এগ্রিগেট ডাটা চেক করার, বা টেবিল জুড়ে তুলনা করার প্রয়োজন হয় (উদাহরণ: "একটি অ্যাকাউন্ট তার প্ল্যান সীমা ছাড়তে পারবে না"), তাহলে সেই লজিক অ্যাপ্লিকেশন কোড, ট্রিগার, বা একটি নিয়ন্ত্রিত ব্যাকগ্রাউন্ড জবে রাখুন।
UNIQUE নিয়ম সহজ: ডাটাবেস একই কলামে (বা একাধিক কলামে সংক্ষেপে) একই মানের দুটি সারি সংরক্ষণ করবে না। এটি সেই বাগগুলোর পুরো শ্রেণি মুছে দেয় যেখানে একটি "create" পথ দু'বার চলে, একটি রিট্রাই ঘটে, বা দুই ব্যবহারকারী একই সময়ে একই তথ্য সাবমিট করে।
UNIQUE নিশ্চিত করে নির্দিষ্ট মানগুলোর জন্য ডুপ্লিকেট নেই। এটি গ্যারান্টি দেয় না যে মানটি উপস্থিত (NOT NULL), যে এটি একটি ফরম্যাট অনুসরণ করে (CHECK), বা যে এটি আপনার সমতুল্য ধারণার সাথে মেলে (কেস, স্পেস, পাংচুয়েশন) যদি না আপনি সেটি নির্দিষ্ট করেন।
সাধারণ জায়গা যেখানে আপনি সাধারণত ইউনিকনেস চান: ইউজার টেবিলের ইমেল, অন্য সিস্টেম থেকে আসা external_id, বা এমন একটি নাম যা অ্যাকাউন্টের মধ্যে ইউনিক হতে হবে যেমন (account_id, name)।
একটি ধরা: NULL ও UNIQUE। PostgreSQL-এ NULL কে "অজানা" মনে করা হয়, তাই একটি UNIQUE কনস্ট্রেইন্টে বহু NULL মান অনুমোদিত। যদি আপনার মানটি থাকা এবং ইউনিকও হতে হবে, তবে UNIQUE এর সঙ্গে NOT NULL মিলান।
ইউজার-ফেসিং আইডেন্টিফায়ারের জন্য একটি ব্যবহারিক প্যাটার্ন হচ্ছে কেস-ইনসেনসিটিভ ইউনিকনেস। মানুষ টাইপ করবে “[email protected]” এবং পরে “[email protected]” প্রত্যাশা করবে যে এগুলো একই।
-- Case-insensitive unique email
CREATE UNIQUE INDEX users_email_unique_ci
ON users (lower(email));
-- Unique contact name per account
ALTER TABLE contacts
ADD CONSTRAINT contacts_account_name_unique UNIQUE (account_id, name);
আপনার ব্যবহারকারীদের জন্য "ডুপ্লিকেট" কী বোঝায় তা নির্ধারণ করুন (কেস, হোয়াইটস্পেস, per-account বনাম global), তারপর একবারে সেটা এনকোড করুন যাতে প্রতিটি কোড-পাথ একই নিয়ম অনুসারে কাজ করে।
একটি FOREIGN KEY বলে, "এই সারিটা ওখানে একটি বাস্তব সারিকে পয়েন্ট করবে।" এর অভাবে, কোড নীরবে অনাথ রেকর্ড তৈরি করতে পারে যা একাকী দেখতে বৈধ মনে হয় কিন্তু পরে অ্যাপ ভেঙে দেয়। উদাহরণ: একটি নোট যা একটি মুছে ফেলা কাস্টমারকে রেফার করে, বা একটি ইনভয়েস যা এমন একটি ইউজার আইডি পয়েন্ট করে যা কখনো ছিলই না।
ফরেন কি সবচেয়ে বেশি গুরুত্বপূর্ণ যখন দুটি অ্যাকশন কাছাকাছি সময়ে ঘটে: একটি ডিলিট এবং একটি ক্রিয়েট, টাইমআউট পরে রিট্রাই, বা একটি ব্যাকগ্রাউন্ড জব স্টেল ডেটা নিয়ে চলে। ডাটাবেস প্রত্যেক অ্যাপ পথকে মনে রাখার চেয়ে বেশি দক্ষভাবে কনসিস্টেন্সি জোরদার করে।
ON DELETE অপশনটি সম্পর্কের বাস্তব-জগতের অর্থের সাথে মিলানো উচিত। জিজ্ঞেস করুন: "যদি প্যারেন্ট অদৃশ্য হয়ে যায়, তাহলে চাইল্ডগুলি কি এখনও থাকা উচিত?"
RESTRICT (বা NO ACTION): প্যারেন্টের চাইল্ড থাকলে প্যারেন্ট ডিলিট ব্লক করুন।CASCADE: প্যারেন্ট ডিলিট করলে চাইল্ডগুলোও মুছে যাবে।SET NULL: চাইল্ড রেখে দিন কিন্তু লিঙ্ক মুছে দিন।CASCADE নিয়ে সতর্ক থাকুন। এটি সঠিক হতে পারে, কিন্তু একটি বাগ বা অ্যাডমিন অ্যাকশন প্যারেন্ট মুছে দিলে এটি আপনি প্রত্যাশা করা ছাড়া অনেক কিছু মুছে ফেলতে পারে।
মাল্টি-টেন্যান্ট অ্যাপগুলোতে ফরেন কি কেবল সঙ্গততার জন্য নয়; এগুলো ক্রস-অ্যাকাউন্ট লিকেজ প্রতিরোধ করে। একটি সাধারণ প্যাটার্ন হল প্রতিটি টেন্যান্ট-অর্থবিশিষ্ট টেবিলে account_id রাখা এবং সম্পর্কগুলো তার মধ্য দিয়ে বেঁধে দেওয়া।
CREATE TABLE contacts (
account_id bigint NOT NULL,
id bigint GENERATED ALWAYS AS IDENTITY,
PRIMARY KEY (account_id, id)
);
CREATE TABLE notes (
account_id bigint NOT NULL,
id bigint GENERATED ALWAYS AS IDENTITY,
contact_id bigint NOT NULL,
body text NOT NULL,
PRIMARY KEY (account_id, id),
FOREIGN KEY (account_id, contact_id)
REFERENCES contacts (account_id, id)
ON DELETE RESTRICT
);
এটি স্কিমায় "কে কী রয়ছে" এনফোর্স করে: একটি নোট একটি ভিন্ন অ্যাকাউন্টের কন্টাক্টকে পয়েন্ট করতে পারবে না, এমনকি অ্যাপ কোড (বা একটি LLM-জেনারেটেড কুয়েরি) চেষ্টা করলেও।
শুরুতে ইনভারিয়েন্টসের একটি সংক্ষিপ্ত তালিকা লিখুন: এমন সত্যগুলো যা সবসময় সত্য থাকতে হবে। সেগুলো সরল রাখুন। "প্রতিটি কন্টাক্টে একটি ইমেল থাকা দরকার।" "একটি স্ট্যাটাস কয়েকটি অনুমোদিত মানের মধ্যে থাকতে হবে।" "একটি ইনভয়েস একটি বাস্তব কাস্টমারের অন্তর্গত হতে হবে।" এই নিয়মগুলোই আপনি ডাটাবেসে এনফোর্স করতে চান।
পরিবর্তনগুলো ছোট মাইগ্রেশনে রোল আউট করুন যাতে প্রোডাকশন চমকপ্রদ না হয়:
NOT NULL, UNIQUE, CHECK, FOREIGN KEY)।আলস্যপূর্ণ অংশ হচ্ছে বিদ্যমান খারাপ ডেটা। এর জন্য পরিকল্পনা করুন। ডুপ্লিকেটের ক্ষেত্রে, একটি বিজয়ী সারি বাছুন, বাকি একত্রিত করুন, এবং একটি ছোট অডিট নোট রাখুন। অনুপস্থিত প্রয়োজনীয় ক্ষেত্রের জন্য, কেবল তখনি একটি নিরাপদ ডিফল্ট বেছে নিন যদি তা সত্যিই নিরাপদ হয়; অন্যথায় কোয়ারেন্টাইন করুন। ভাঙা সম্পর্কের জন্য, চাইল্ড সারিগুলো সঠিক প্যারেন্টে পুনঃনির্ধারিত করুন বা খারাপ সারিগুলো মুছে ফেলুন।
প্রতিটি মাইগ্রেশনের পরে, কয়েকটি লেখার সাথে ভ্যালিডেট করুন যেগুলো ব্যর্থ হওয়া উচিত: একটি অনুপস্থিত প্রয়োজনীয় মান সহ সারি ইনসার্ট করুন, একটি ডুপ্লিকেট কী ইনসার্ট করুন, একটি আউট-অফ-রেঞ্জ মান ইনসার্ট করুন, এবং একটি অনুপস্থিত প্যারেন্ট রেকর্ড রেফার করে ইনসার্ট করুন। ব্যর্থ লেখাগুলো দরকারী সংকেত। এগুলো দেখায় কোন কোন জায়গায় অ্যাপ "বেস্ট এফোর্ট" আচরণে নির্ভর ছিল।
একটি ছোট CRM কল্পনা করুন: accounts (আপনার SaaS-এর প্রতিটি গ্রাহক), তাদের কাজ করা companies, ঐ কোম্পানির contacts, এবং deals কোম্পানির সাথে যুক্ত।
এটাই ঠিক সেই ধরনের অ্যাপ যা মানুষ দ্রুত একটি চ্যাট টুল দিয়ে জেনারেট করে। ডেমোতে ঠিক থাকলেও বাস্তব ডেটা দ্রুত নোংরা হয়ে যায়। দু'টি বাগ সাধারণত শীঘ্রই দেখা দেয়: ডুপ্লিকেট কন্টাক্ট (একই ইমেল সামান্য ভিন্নভাবে প্রবেশ করা), এবং deals যা company ছাড়া তৈরি হয় কারণ কোনও কোড-পাথ company_id সেট করতে ভুলে গেছে। আরেকটি ক্লাসিক হলো একটি নেগেটিভ deal value একটি রিফ্যাক্টর বা পার্সিং ভুলের পরে।
সমাধানটি আরও if-statements নয়। এটা কয়েকটি ভালোভাবে বেছে নেওয়া বিধিনিষেধ যা খারাপ ডেটা সংরক্ষণই অসম্ভব করে।
-- Contacts: prevent duplicates per account
ALTER TABLE contacts
ADD CONSTRAINT contacts_account_email_uniq UNIQUE (account_id, email);
-- Deals: require a company and keep the relationship valid
ALTER TABLE deals
ALTER COLUMN company_id SET NOT NULL,
ADD CONSTRAINT deals_company_fk
FOREIGN KEY (company_id) REFERENCES companies(id);
-- Deals: deal value cannot be negative
ALTER TABLE deals
ADD CONSTRAINT deals_value_nonneg CHECK (deal_value >= 0);
-- A few obvious required fields
ALTER TABLE companies
ALTER COLUMN name SET NOT NULL;
ALTER TABLE contacts
ALTER COLUMN email SET NOT NULL;
এটি কেবল কড়াকড়ি করার জন্য না। আপনি অনিশ্চিত প্রত্যাশাগুলোকে এমন নিয়মে রূপান্তর করছেন যা ডাটাবেস প্রতিবারই এনফোর্স করবে, যেখানেই অ্যাপ ডেটা লিখুক।
একবার এই বিধিনিষেধগুলো থাকলে, অ্যাপ সহজ হয়। আপনি অনেক ডিফেন্সিভ চেক সরিয়ে ফেলতে পারেন যা পরে ডুপ্লিকেট সনাক্ত করার চেষ্টা করে। ব্যর্থতা পরিষ্কার ও কার্যকর হয়ে যায় (উদাহরণ: "এই অ্যাকাউন্টের জন্য ইমেল ইতিমধ্যেই আছে" বরং অদ্ভুত ডাউনস্ট্রীম আচরণ)। এবং যখন একটি জেনারেটেড API রুট একটি ফিল্ড ভুলে যায় বা একটি মান মিসহ্যান্ডল করে, তখন লেখাটি অবিলম্বে ব্যর্থ হয়, নীরবে ডাটাবেস করাপ্ট না করে।
বিধিনিষেধ তখনই ভালো কাজ করে যখন এগুলো ব্যবসার বাস্তব কাজ করার পদ্ধতির সাথে মিলে। অধিকাংশ কষ্ট আসে তখন যখন এমন নিয়ম যোগ করা হয় যা মুহূর্তে "নিরাপদ" মনে হয় কিন্তু পরে এরা অবাক করে।
একটি সাধারণ ফুট-গান হলো ON DELETE CASCADE সবখানে ব্যবহার করা। এটা সুসঙ্গত দেখায় যতক্ষণ না কেউ একটি প্যারেন্ট মুছে ফেলে এবং ডাটাবেস আপনার সিস্টেমের অর্ধেক মুছে ফেলে। Cascades সত্যিই_owned_ ডেটার জন্য সঠিক হতে পারে (যেমন ড্রাফট লাইনের আইটেম যা কখনই আলোকস্বতন্ত্রভাবে থাকা উচিত নয়), কিন্তু কাস্টমার, ইনভয়েস, টিকিট মত গুরুত্বপূর্ণ রেকর্ডের জন্য ঝুঁকিপূর্ণ। যদি আপনি নিশ্চিত না হন, RESTRICT-কে পছন্দ করুন এবং ডিলিটগুলো সচেতনভাবে হ্যান্ডেল করুন।
আরেকটি সমস্যা হলো এতটাই সংকীর্ণ CHECK লেখা যে ভবিষ্যতে বৈধ কেস ব্লক হয়ে যায়। "Status হওয়া উচিত ‘new’, ‘won’, বা ‘lost’" শুনতে ভালো লাগে যতক্ষণ না আপনাকে পরে "paused" বা "archived" দরকার হয়। একটি ভালো CHECK কনস্ট্রেইন্ট একটি স্থায়ী সত্য বর্ণনা করে, অস্থায়ী UI পছন্দ নয়। "amount >= 0" ভালোভাবে পুরনো হবে। "country in (...)" প্রায়ই হবে না।
কয়েকটি সমস্যা বারবার দেখা যায় যখন টিমগুলো জেনারেটেড কোড চালু করার পর বিধিনিষেধ যোগ করে:
CASCADE-কে একটি ক্লিনআপ টুল হিসেবে ব্যবহার করা, ফলশ্রুতিতে অপ্রত্যাশিতভাবে বেশি ডেটা মুছে ফেলা।পারফরম্যান্স সম্পর্কে: PostgreSQL স্বয়ংক্রিয়ভাবে UNIQUE-এর জন্য একটি ইন্ডেক্স তৈরি করে, কিন্তু ফরেন কি রেফারেন্সিং কলামে স্বয়ংক্রিয়ভাবে ইন্ডেক্স তৈরি করে না। সেই ইন্ডেক্স না থাকলে প্যারেন্টে আপডেট ও ডিলিট ধীর হতে পারে কারণ Postgres চাইল্ড টেবিল স্ক্যান করে রেফারেন্স চেক করতে হবে।
কঠোর করার আগে, এমন বিদ্যমান সারি খুঁজে বের করুন যা সেটি ব্যর্থ করবে, নির্ধারণ করুন সেগুলো ঠিক করবেন নাকি কোয়ারেন্টাইন করবেন, এবং পরিবর্তনগুলো ধাপে ধাপে রোলআউট করুন।
শিপ করার আগে, প্রতিটি টেবিলের জন্য পাঁচ মিনিট নিয়ে লিখুন কি কি সবসময় সত্য থাকা উচিত। যদি আপনি এটা সাধারণ ইংরেজিতে বলতে পারেন, সাধারণত আপনি এটাকে একটি কনস্ট্রেইন্ট দিয়ে এনফোর্স করতে পারবেন।
প্রতিটি টেবিলের জন্য এই প্রশ্নগুলো জিজ্ঞেস করুন:
আপনি যদি একটি চ্যাট-ড্রাইভেন বিল্ড টুল ব্যবহার করেন, এসব ইনভারিয়েন্টকে ডেটার জন্য অ্যাকসেপ্টেন্স ক্রাইটেরিয়া হিসেবে বিবেচনা করুন, না যে কেবল ঐচ্ছিক নোট। উদাহরণ: "একটি deal amount কখনই নেগেটিভ হবে না", "একটি contact ইমেল workspace প্রতি ইউনিক", "একটি task অবশ্যই একটি বাস্তব contact রেফার করবে।" যত বেশি স্পষ্ট নিয়ম থাকবে, তত কম দুর্ঘটনাজনিত এজ-কেসের সুযোগ থাকবে।
Koder.ai (koder.ai) includes features like planning mode, snapshots and rollback, and source code export, which can make it easier to iterate on schema changes safely while you tighten constraints over time.
একটি সরল রোলআউট প্যাটার্ন যা বাস্তব টিমে কাজ করে: একটি উচ্চ-মুল্য টেবিল বেছে নিন (users, orders, invoices, contacts), 1-2 বিধিনিষেধ যোগ করুন যা সবচেয়ে বড় ব্যর্থতাগুলো প্রতিরোধ করে (প্রায়ই NOT NULL ও UNIQUE), যে লেখাগুলো ব্যর্থ হচ্ছে সেগুলো ঠিক করুন, তারপর পুনরাবৃত্তি করুন। সময়ের সাথে নিয়ম কঠোর করা এক বড় ঝুঁকিপূর্ণ মাইগ্রেশনের চাইতে ভালো।