২২ অক্টো, ২০২৫·8 মিনিট পড়তে

পুনর্জেনারেশন-নিরাপদ স্কিমা বিবর্তন: পূর্বানুমানযোগ্য মাইগ্রেশন

পুনরায়-জেনারেশনের সময় প্রোডাকশন ডেটা বৈধ রাখার জন্য নিরাপদ স্কিমা বিবর্তন। স্কিমা পরিবর্তন ও মাইগ্রেশন পরিকল্পনা করার একটি ব্যবহারিক উপায় শিখুন।

পুনর্জেনারেশন-নিরাপদ স্কিমা বিবর্তন: পূর্বানুমানযোগ্য মাইগ্রেশন

কেন পুনরায়-জেনারেট করা ব্যাকএন্ডে স্কিমা পরিবর্তন ঝুঁকিপূর্ণ মনে হয়

যখন আপনার ব্যাকএন্ড ভিজুয়াল মডেল থেকে পুনরায়-জেনারেট করা হয়, তখন একটি ডাটাবেস পরিবর্তন একটি সোয়েটারের সুঁতি টেনে বের করার মতো মনে হতে পারে। আপনি Data Designer-এ একটি ফিল্ড আপডেট করেন, রিজেনারেট বোতামে ক্লিক করেন, এবং হঠাৎ বুঝতে পারেন আপনি কেবল একটি টেবিলই বদলাচ্ছেন না। একই সঙ্গে জেনারেট হওয়া API, ভ্যালিডেশন নিয়ম, এবং আপনার অ্যাপ যে প্রশ্নগুলোর মাধ্যমে পড়ে/লিখে—সবকিছু বদলে যাচ্ছে।

সাধারণত যা ভুল হয় তা হলো নতুন কোড বিল্ডে ব্যর্থ হয়—এটা নয়। অনেক নো-কোড প্ল্যাটফর্ম (AppMaster সহ, যা ব্যাকএন্ডের জন্য আসল Go কোড জেনারেট করে) প্রতিবার একটি পরিষ্কার প্রজেক্ট জেনারেট করবে। প্রকৃত ঝুঁকি হল প্রোডাকশনে ইতিমধ্যে ডেটা আছে, এবং তা নিজে থেকেই আপনার নতুন ধারণার সঙ্গে খাপ খায় না।

মানুষ প্রথমেই দুই ধরণের সমস্যা লক্ষ্য করে:

  • ভাঙা পড়া (Broken reads): কোনও কলাম সরানো, টাইপ বদলানো, বা প্রশ্ন এমন কিছু প্রত্যাশা করা—যা নেই—তার ফলে অ্যাপ আর রেকর্ড লোড করতে পারে না।
  • ভাঙা লেখা (Broken writes): নতুন বা আপডেট করা রেকর্ড ব্যর্থ হয় কারণ কনস্ট্রেইন্ট, প্রয়োজনীয় ফিল্ড, বা ফরম্যাট বদলে গেছে, এবং বিদ্যমান ক্লায়েন্টরা এখনও পুরনো শেপ পাঠায়।

উভয় ফলাফল কষ্টদায়ক কারণ এগুলো প্রকৃত ব্যবহারকারীরা অ্যাকশনে আসা পর্যন্ত লুকিয়ে থাকতে পারে। স্টেজিং ডাটাবেস খালি বা সদ্য সীড করা থাকতে পারে, তাই সবকিছু ঠিক দেখায়। প্রোডাকশনে এজ কেস থাকে: আপনি যেখানে ভ্যালু আশা করেছিলেন সেখানে null, পুরনো enum স্ট্রিং, অথবা এমন সারি যেগুলো নতুন নিয়মের আগেই তৈরি হয়েছে।

এই কারণেই পুনর্জেনারেশন-নিরাপদ স্কিমা বিবর্তন গুরুত্বপূর্ণ। লক্ষ্য হলো প্রতিটি পরিবর্তনকে এমনভাবে করা যাতে ব্যাকএন্ড সম্পূর্ণভাবে রিজেনারেট হলেও পুরনো রেকর্ডগুলো বৈধ থাকে এবং নতুন রেকর্ডও তৈরি করা যায়।

“পূর্বানুমানযোগ্য মাইগ্রেশন” মানে হলো আপনি ডেপ্লয় করার আগে চারটি প্রশ্নের উত্তর দিতে পারবেন: ডাটাবেসে কী বদলাবে, বিদ্যমান রো গুলোর কী হবে, রোলআউটের সময় কোন ভার্সনের অ্যাপ কাজ করতে পারবে, এবং যদি অপ্রত্যাশিত কিছু দেখা দেয় তাহলে কিভাবে রোলব্যাক করবেন।

একটি সহজ মডেল: স্কিমা, মাইগ্রেশন, এবং পুনরায়-জেনারেট করা কোড

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

পুনরায়-জেনারেশনকে ভাবুন “সর্বশেষ মডেল থেকে অ্যাপ্লিকেশন কোড পুনর্নির্মাণ” হিসেবে। AppMaster-এর মতো টুলে এই পুনরায়-জেনারেশন কাজের সময় অনেকবার ঘটতে পারে: আপনি একটি ফিল্ড সামান্য বদলান, বিজনেস লজিক সামঞ্জস্য করেন, একটি এন্ডপয়েন্ট যোগ করেন, রিজেনারেট করেন, টেস্ট করেন, আবার। রিজেনারেশন ঘনঘন হয়। আপনার প্রোডাকশন ডাটাবেস হওয়া উচিত না।

এখানে সহজ মডেলটা:

  • স্কিমা: টেবিল, কলাম, ইনডেক্স এবং কনস্ট্রেইন্টের কাঠামো। এটি ডাটাবেস যা প্রত্যাশা করে।
  • মাইগ্রেশন: অর্ডারকৃত, পুনরাবর্তিত ধাপগুলো যা স্কিমাকে একটি ভার্সন থেকে পরের ভার্সনে নিয়ে যায় (কখনও কখনও ডেটাও সরিয়ে/টানায়)। এটিই আপনি প্রতিটি এনভায়রনমেন্টে চালান।
  • রানটাইম ডেটা: ব্যবহারকারী এবং প্রসেস দ্বারা তৈরি বাস্তব রেকর্ড। পরিবর্তনের আগে, সময়ে এবং পরে এগুলো বৈধ থাকতে হবে।

পুনরায়-জেনারেট করা কোডকে বিবেচনা করুন “কারেন্ট অ্যাপ যা কারেন্ট স্কিমার সঙ্গে কথা বলে।” মাইগ্রেশন হলো সেই সেতু যা কোড বদলালেও স্কিমা এবং রানটাইম ডেটাকে সম্মিলিত রাখে।

কেন পুনরায়-জেনারেশন খেলাটাকে বদলে দেয়

আপনি যদি ঘনঘন রিজেনারেট করেন, আপনি স্বাভাবিকভাবেই অনেক ছোট স্কিমা এডিট করবেন। এটাই স্বাভাবিক। ঝুঁকি তখন দেখা দেয় যখন ওই এডিটগুলো এমন ডাটাবেস বদল প্রস্তাব করে যা ব্যাকওয়ার্ড-কম্প্যাটিবল নয়, বা যখন আপনার মাইগ্রেশন ডিটারমিনিস্টিক নয়।

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

উদাহরণস্বরূপ, যদি আপনি লাইভ API-তে ব্যবহৃত একটি কলামের নাম বদলাতে চান, তাৎক্ষণিকভাবে নাম বদলাবেন না। আগে নতুন কলাম যোগ করুন, দু'দিকে লেখার লজিক দিন, বিদ্যমান সারিগুলো ব্যাকফিল করুন, তারপর পাঠ (reads) নতুন কলাম থেকে নিন। তারপরই পুরনো কলাম বাদ দিন। প্রতিটি ধাপ পরীক্ষা করা সহজ, এবং কিছু ভুল হলে আপনি ডেটা করাপ্ট না করেই বিরতি দিতে পারেন।

এই মানসিক মডেল মাইগ্রেশনকে পূর্বানুমানযোগ্য করে তোলে, এমনকি কোড রিজেনারেশন প্রতিদিন ঘটলেও।

স্কিমা পরিবর্তনের প্রকারভেদ এবং কোনগুলো প্রোডাকশন ভাঙে

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

কিছু পরিবর্তন স্বাভাবিকভাবেই নিরাপদ কারণ তা বিদ্যমান সারি বা প্রশ্নকে অকার্যকর করে না। অন্যগুলো ডেটার অর্থ বদলে দেয় বা এমন কিছু সরিয়ে দেয় যা চলমান অ্যাপ এখনও প্রত্যাশা করে—এগুলোই প্রোডাকশন ইস্যু তৈরি করে।

নিম্ন-ঝুঁকিপূর্ণ, সাধারণত নিরাপদ (অ্যাডিটিভ)

অ্যাডিটিভ পরিবর্তনগুলো চালানো সহজ কারণ তা পুরনো ডেটার সঙ্গে সহঅবস্থান করতে পারে।

  • এমন নতুন টেবিল যেটার উপর এখনও কেউ নির্ভর করে না।
  • কোন ডিফল্ট প্রয়োজন নেই এমন নতুন Nullable কলাম।
  • সম্পূর্ণ অপশনাল নতুন API ফিল্ড।

উদাহরণ: একটি users টেবিলে nullable middle_name কলাম যোগ করা সাধারণত নিরাপদ। বিদ্যমান সারিগুলো বৈধ থাকে, রিজেনারেট করা কোড যখন উপস্থিত থাকবে তখন তা পড়তে পারবে, এবং পুরনো সারিগুলোতে কেবল NULL থাকবে।

মধ্যম-ঝুঁকিপূর্ণ (অর্থ পরিবর্তন)

এই পরিবর্তনগুলো প্রায়ই টেকনিক্যালি “চলে” কিন্তু আচরণ ভেঙে দেয়। এগুলো সমন্বয়ের প্রয়োজন কারণ রিজেনারেশন ভ্যালিডেশন, জেনারেটেড মডেল, এবং বিজনেস লজিক অনুমান আপডেট করে।

রেনেমস (নাম পরিবর্তন) ক্লাসিক ফাঁদ: phone-কে mobile_phone করলে রিজেনারেট কোড আর phone পড়বে না, অথচ প্রোডাকশনে ডেটা সেখানে আছে। একইভাবে ইউনিট পরিবর্তন (মূল্য ডলার vs সেন্টে) কোড পরিবর্তনের আগে বা ডেটা পরিবর্তনের আগে হলে চুপচাপ হিসাব নষ্ট করতে পারে।

Enums আরেকটি ধারালো এলাকা। একটি enum কে সংকীর্ণ করা (মানগুলো তুলে ফেলা) বিদ্যমান সারিকে অবৈধ করে দিতে পারে। enum বাড়ানো সাধারণত নিরাপদ, কিন্তু তখন নিশ্চিত করুন সব কোডপাথ নতুন মান সামলাতে পারে।

ব্যবহারিক পন্থা হলো অর্থ পরিবর্তনগুলোকে “নতুন যোগ করুন, ব্যাকফিল করুন, সুইচ করুন, পরে সরান” হিসেবে বিবেচনা করা।

উচ্চ-ঝুঁকিপূর্ণ (ধ্বংসাত্মক)

ধ্বংসাত্মক পরিবর্তনগুলোই সাধারণত প্রোডাকশনকে অবিলম্বে ভাঙে, বিশেষত যখন প্ল্যাটফর্ম কোড রিজেনারেট করে এবং পুরনো শেপ প্রত্যাশা বন্ধ করে দেয়।

কোনো কলাম বা টেবিল ড্রপ করা, অথবা কলামকে nullable থেকে not-null করা এমন সময়ই লেখা ব্যর্থ হতে পারে যখনই কোনও অনুরোধ ঐ মান ছাড়া ইনসার্ট করে। এমনকি আপনি মনে করলে “সব রোতেই এটা আছে,” পরের কোন এজ কেস বা ব্যাকগ্রাউন্ড জব তা প্রমাণ করে দেয়।

আপনি যদি not-null পরিবর্তন করতে বাধ্য হন, তা ধাপে করুন: প্রথমে কলাম nullable হিসেবে যোগ করুন, ব্যাকফিল করুন, অ্যাপ লজিক আপডেট করে সবসময় সেট করবে এমন করুন, তারপর বার করে not-null দিন।

পারফরম্যান্স ও সেফটি পরিবর্তন (লিখা ব্লক করতে পারে)

ইনডেক্স এবং কনস্ট্রেইন্টগুলো "ডেটা শেপ" নয়, তবুও এগুলো ডাউনটাইম ঘটাতে পারে। বড় টেবিলে ইনডেক্স তৈরি করা বা ইউনিক কনস্ট্রেইন্ট যোগ করা লেখাকে যথেষ্ট সময়ের জন্য লক করে দিতে পারে। PostgreSQL-এ কিছু অপারেশন অনলাইনে করার পদ্ধতি থাকে, কিন্তু মূল পয়েন্ট হল সময়: ভারী অপারেশনগুলো কম ট্রাফিক সময়েই করুন এবং স্টেজিং কপিতে সময় পরিমাপ করুন।

যখন পরিবর্তনগুলো প্রোডাকশনে অতিরিক্ত যত্ন দাবি করে, তখন পরিকল্পনায় রাখুন:

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

আপনি যদি AppMaster-এর মতো প্ল্যাটফর্ম ব্যবহার করেন যেখানে Data Designer থেকে ব্যাকএন্ড জেনারেট হয়, সবচেয়ে নিরাপদ মানসিকতা হলো: এমন পরিবর্তনই পাঠান যা আজ পুরনো ডেটা সহ্য করতে পারে, তারপর সিস্টেম খাপ খাওয়ালে নিয়ম কঠোর করুন।

পুনরায়-জেনারেশন-নিরাপদ পরিবর্তনের মূলনীতি

রিজেনারেট করা ব্যাকএন্ডগুলো দুর্দান্ত—যতক্ষণ না একটা স্কিমা পরিবর্তন প্রোডাকশনে ল্যান্ড করে এবং পুরনো সারিগুলো নতুন শেপে মেলে না। পুনর্জেনারেশন-নিরাপদ স্কিমা বিবর্তনের লক্ষ্য সহজ: আপনার অ্যাপ কাজ করে থাকুক যখন আপনার ডাটাবেস ও রিজেনারেট হওয়া কোড ধাপে ধাপে একে অপরের সাথে খাপ খাওয়ায়।

ডিফল্টভাবে “বড়ান, মাইগ্রেট, সংকুচিত” পন্থা

প্রতিটি অর্থপূর্ণ পরিবর্তনকে তিনটি ধাপে বিবেচনা করুন। প্রথমে স্কিমা বর্ধিত করুন যাতে পুরনো ও নতুন কোড দুই-ই চলতে পারে। তারপর ডেটা মাইগ্রেট করুন। শেষপর্যন্ত পুরনো কলাম, ডিফল্ট বা কনস্ট্রেইন্ট সরিয়ে দিয়ে সংকোচন করুন।

প্রয়োগযোগ্য নিয়ম: কখনই "নতুন স্ট্রাকচার" এবং "ব্রেকিং ক্লিনআপ" একসঙ্গে একই ডেপ্লয় করুন না।

কিছু সময় পুরনো ও নতুন শেপ সমর্থন রাখুন

ধারণা করুন একটি সময় থাকবে যেখানে:

  • কিছু রেকর্ডে নতুন ফিল্ড আছে, কিছুতে নেই
  • কিছু অ্যাপ ইনস্ট্যান্স পুরানো কোড চালায়, কিছু রিজেনারেট করা কোড
  • ব্যাকগ্রাউন্ড জব, ইমপোর্ট বা মোবাইল ক্লায়েন্ট পিছিয়ে থাকতে পারে

আপনার ডাটাবেস এমনভাবে ডিজাইন করুন যাতে ওভারল্যাপ সময়ে দুই শেপই বৈধ থাকে। যখন প্ল্যাটফর্ম আপনার ব্যাকএন্ড Data Designer থেকে রিজেনারেট করে, এটা আরও বেশি গুরুত্বপূর্ণ।

লেখার আগে পড়া সামঞ্জস্য করুন

নতুন কোডকে আগে পুরনো ডেটা নিরাপদে পড়তে সক্ষম করুন। তারপরই লিখার পথগুলো নতুন শেপে পাঠাতে শুরু করুন।

উদাহরণ: যদি আপনি একটি একক status ফিল্ডকে status + status_reason-এ বিভক্ত করেন, এমন কোড পাঠান যা অনুপস্থিত status_reason-কে সামলাতে পারে প্রথমে। পরে নতুন আপডেটে status_reason লিখা শুরু করুন।

আংশিক ও অজানা ডেটা নিয়ে সিদ্ধান্ত নিন

আপনি যখন enum, non-null কলাম বা শক্ত কনস্ট্রেইন্ট যোগ করেন, সিদ্ধান্ত নিন অনুপস্থিত বা অপ্রত্যাশিত মানের ক্ষেত্রে কী হবে:

  • সাময়িকভাবে null রাখতে পারেন, পরে ব্যাকফিল করবেন
  • এমন একটি নিরাপদ ডিফল্ট দিন যা মীনের পরিবর্তন করে না
  • পড়া ব্যর্থ না করতে “unknown” মান রাখুন

এটি চুপচাপ করাপশন (ভুল ডিফল্ট) এবং হঠাৎ ব্যর্থতা (নতুন কনস্ট্রেইন্ট পুরোনো সারি ঠুকবে) প্রতিরোধ করে।

প্রতিটি ধাপের জন্য রোলব্যাক স্টোরি রাখুন

এক্সপ্যান্ড ধাপে রোলব্যাক সবচেয়ে সহজ। যদি আপনি রিভার্ট করতে চান, পুরনো কোডকে বিস্তৃত স্কিমার বিরুদ্ধে চালাতে পারবেন। লিখে রাখুন আপনি কী রোলব্যাক করবেন (শুধু কোড, না কোড + মাইগ্রেশন), এবং ধ্বংসাত্মক পরিবর্তনগুলি এড়িয়ে চলুন যতক্ষণ না আপনি নিশ্চিত যে রিভার্ট করার দরকার নেই।

ধাপে ধাপে: একটি পরিবর্তন পরিকল্পনা করুন যা পুনর্জেনারেশন টেকে

স্কিমা থেকে কার্যকর API পর্যন্ত যান
আপনার ডাটা মডেলকে API এবং ব্যবসায়িক লগিকে বদলে নিন, প্রয়োজন অনুযায়ী সমন্বয় করুন।
অ্যাপ তৈরি করুন

রিজেনারেট করা ব্যাকএন্ড অনুকর্য: যদি স্কিমা ও জেনারেট কোড একমত না হয়, প্রোডাকশন সাধারণত প্রথমে সমস্যাটা খুঁজে পায়। সবচেয়ে নিরাপদ পন্থা হল প্রতিটি পরিবর্তনকে ছোট, উল্টে নেওয়া যায় এমন রোলআউট হিসেবে দেখা—even যদি আপনি নো-কোড টুলে কাজ করেন।

শুরুতে সাধারণ ভাষায় উদ্দেশ্য লিখে রাখুন এবং আপনার ডেটা এখন কেমন তা নোট করুন। প্রোডাকশনের 3–5টি বাস্তব রোPick করে নিন (বা সাম্প্রতিক ডাম্প) এবং বিভ্রান্তিকর অংশগুলো লিপিবদ্ধ করুন: খালি মান, পুরনো ফরম্যাট, অপ্রত্যাশিত ডিফল্ট। এটি প্রতিরোধ করে যে আপনি এমন একটি আদর্শ নতুন ফিল্ড ডিজাইন করবেন যা বাস্তব ডেটা পূরণ করতে পারে না।

নিচে একটি প্রায়োগিক ক্রম যা এমন প্ল্যাটফর্মে কাজ করে যেখানে ব্যাকএন্ড রিজেনারেট হয় (উদাহরণস্বরূপ, AppMaster যখন Data Designer-এ মডেল আপডেট করে এবং Go ব্যাকএন্ড সার্ভিস রিজেনারেট করে):

  1. প্রথমে বড়ান, প্রতিস্থাপন না করুন। নতুন কলাম বা টেবিল অ্যাডিটিভভাবে যোগ করুন। নতুন ফিল্ড প্রথমে nullable রাখুন অথবা নিরাপদ ডিফল্ট দিন। যদি নতুন সম্পর্ক আনেন, তখন ফরেন কি খালি রাখতে দিন যতক্ষণ নাPopulate করা হবে।

  2. কিছু মুছে ফেলা ছাড়া বিস্তৃত স্কিমা ডেপ্লয় করুন। পুরনো কোড এখনও কাজ করবে এমন অবস্থায় ডাটাবেস পরিবর্তন পাঠান। লক্ষ্য হলো: পুরনো কোড পুরোনো কলামগুলো লিখতেই পারে, এবং ডাটাবেস সেটি গ্রহণ করে।

  3. নিয়ন্ত্রিত জবে ব্যাকফিল করুন। নতুন ফিল্ডগুলো ব্যাচ প্রসেসে ভরুন যা মনিটর এবং পুনরায় চালানো যায়। এটি idempotent রাখুন (দুইবার চালালেও ডেটা করাপ্ট না হয়)। বড় টেবিল হলে ধীরে ধীরে করুন এবং কতগুলো সারি আপডেট হয়েছে লগ রাখুন।

  4. প্রথমে রিড সুইচ করুন, ফ্যালব্যাক রেখে। রিজেনারেট করা লজিক আপডেট করুন যাতে তা নতুন ফিল্ডকে প্রাধান্য দেয়, কিন্তু নতুন ডেটা অনুপস্থিত হলে পুরনো কলামে fallback থাকে। রিড স্থির হলে, তারপর লেখাগুলো নতুন ফিল্ডে সুইচ করুন।

  5. শেষে পরিষ্কার করুন। কনফিডেন্স এলে (এবং রোলব্যাক প্ল্যান থাকা অবস্থায়) পুরনো ফিল্ডগুলো সরান ও কনস্ট্রেইন্ট কঠোর করুন: NOT NULL দিন, ইউনিক কনস্ট্রেইন্ট যোগ করুন, এবং ফরেনকি কার্যকর করুন।

কংক্রিট উদাহরণ: আপনি একটি মুক্ত-টেক্সট status কলামকে statuses টেবিলকে ইঙ্গিতকারী status_id দিয়ে প্রতিস্থাপন করতে চান। প্রথমে nullable status_id যোগ করুন, বিদ্যমান টেক্সট থেকে ব্যাকফিল করুন, অ্যাপ আপডেট করুন যাতে status_id পড়ে কিন্তু null হলে status থেকে fallback নেয়, তারপর শেষে status ড্রপ করে status_id required করুন। প্রতিটি ধাপ ডাটাবেসকে প্রতিটি পর্যায়ে সামঞ্জস্যপূর্ণ রাখে, তাই প্রতিটি রিজেনারেশন নিরাপদ হয়।

ব্যবহারযোগ্য প্যাটার্নগুলো যা আপনি পুনরায় ব্যবহার করতে পারেন

পূর্বানুমানযোগ্য মাইগ্রেশন অনুশীলন করুন
স্টেজিং-প্রস্তুত ব্যাকএন্ড তৈরি করুন এবং ছোট, উল্টে নেওয়া যায় এমন ধাপে ফিল্ড এগিয়ে নিন।
ডেভেলপ শুরু করুন

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

প্যাটার্ন 1: নন-ব্রেকিং রিনেম

সরাসরি নাম বদল ঝুঁকিপূর্ণ কারণ পুরনো রেকর্ড ও পুরনো কোড প্রায়ই পুরোনো ফিল্ডটি আশা করে। নিরাপদ পন্থা হলো রিনেমকে ছোট মাইগ্রেশন কালের মতো বিবেচনা করা।

  • নতুন কলাম যোগ করুন (উদাহরণ customer_phone) এবং পুরনোটুকু রাখুন (phone)।
  • লজিক আপডেট করে ডুয়াল-রাইট করুন: সেভ করার সময় উভয় ফিল্ডে লিখুন।
  • বিদ্যমান সারিগুলো ব্যাকফিল করুন যাতে customer_phone-এ সব কভারেজ থাকে।
  • কভারেজ উচ্চ হলে রিড নতুন কলামে সুইচ করুন।
  • পরে পুরনো কলাম ড্রপ করুন।

AppMaster-এর মতো টুলে যেখানে রিজেনারেশন মডেল ও এন্ডপয়েন্টগুলো পুনর্নির্মাণ করে, এই ডুয়াল-রাইট পদ্ধতি পুরনো ও নতুন উভয় কোডকে সুখী রাখে।

প্যাটার্ন 2: একটি ফিল্ডকে দুই ভাগে ভাগ করা

full_name-কে first_namelast_name-এ ভাগ করা অনুরূপ, কিন্তু ব্যাকফিল কিছুটা জটিল। full_name রাখুন যতক্ষণ না বিভাজন সম্পূর্ণ নিশ্চিত না হন।

ব্যবহারিক নিয়ম: যখন parsing ব্যর্থ হয়, পুরো স্ট্রিং last_name-এ রাখুন এবং রেকর্ডটি রিভিউর জন্য ফ্ল্যাগ করুন। মূল ফিল্ড সরানোর আগে প্রতিটি রেকর্ড বা স্পষ্ট fallback নিশ্চিত করুন।

প্যাটার্ন 3: একটি ফিল্ডকে আবশ্যক করা

nullable থেকে required করা ক্লাসিক ব্রেকিং পরিবর্তন। নিরাপদ ক্রম হলো: প্রথমে ব্যাকফিল, তারপর enforce করা।

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

প্যাটার্ন 4: enum নিরাপদে পরিবর্তন করুন

Enum পরিবর্তন তখনই ভেঙে যখন পুরানো কোড এখনও পুরনো মান নিয়ে যায়। Transition চলাকালীন সময় দুই-ই গ্রহণ করে এমন করার চেষ্টা করুন। যদি আপনি "pending"কে "queued"-এ বদলান, দুইটা মানই সাময়িকভাবে বৈধ রাখুন এবং লজিকে মান ম্যাপ করুন। সব ক্লায়েন্ট পুরনো মান পাঠানো বন্ধ করলে পরে পুরনো মান সরান।

যদি পরিবর্তন এক রিলিজেই কোরে পাঠাতে হয়, ঝুঁকি কমাতে ব্লাস্ট রেডিয়াস সংকুচিত করুন:

  • পুরনোগুলো রাখুন, এমনকি অপ্রয়োগী হলেও।
  • ডাটাবেস ডিফল্ট রাখুন যাতে ইনসার্ট কাজ চালায়।
  • কোড মনোপলি করুন: নতুন পড়ুন, পুরনোতে fallback।
  • সাময়িক ম্যাপিং লেয়ার যোগ করুন (পুরনো ইন, নতুন স্টোর)।

এই প্যাটার্নগুলো রিজেনারেট হওয়া কোড দ্রুত বদলে গেলে মাইগ্রেশনকে পূর্বানুমানযোগ্য রাখে।

সাধারণ ভুলগুলো যা বিস্ময় সৃষ্টি করে

সবচেয়ে বড় বিস্ময় ঘটে যখন মানুষ কোড রিজেনারেশনকে একটি জাদুকরী রিসেট বোতামের মতো মনে করে। রিজেনারেট করা ব্যাকএন্ড আপনার অ্যাপ কোডকে পরিষ্কার রাখতে পারে, কিন্তু প্রোডাকশন ডাটাবেস এখনও গতকালের ডেটা রাখে, গতকালের শেপে। পুনরায়-জেনারেশন-নিরাপদ স্কিমা বিবর্তন মানে আপনি দুটোই পরিকল্পনা করবেন: নতুন জেনারেট হওয়া কোড এবং টেবিলে বসে থাকা পুরনো রেকর্ডগুলো।

একটি সাধারণ ফাঁদ হলো প্ল্যাটফর্ম যে মাইগ্রেশনগুলো "নিয়ন্ত্রণ করবে" তা ধরে নেওয়া। উদাহরণস্বরূপ, AppMaster-এ আপনি Data Designer মডেল আপডেট করে Go ব্যাকএন্ড রিজেনারেট করতে পারেন, কিন্তু প্ল্যাটফর্ম কিভাবে বাস্তব কাস্টমার ডেটা ট্রান্সফর্ম হবে তা অনুমান করতে পারে না। আপনি যদি একটি নতুন required ফিল্ড যোগ করেন, তখনও আপনাকে স্পষ্ট পরিকল্পনা করতে হবে বিদ্যমান সারিগুলো কীভাবে মান পাবে।

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

এখানে পাঁচটি ভুল যা অতি রাত পর্যন্ত রোলব্যাক দরকার পড়ায়:

  • স্কিমা বদলিয়ে কোড রিজেনারেট করা, কিন্তু ডেটা মাইগ্রেশন লিখে বা যাচাই না করা।
  • প্রতিটি রিডার ও রাইটার আপডেট ও ডেপ্লয় হওয়ার আগে কলাম রিনেম বা মুছে ফেলা।
  • বড় টেবিলে ব্যাকফিল করা আগে সময় কত লাগবে তা না চেক করা।
  • প্রথমে নতুন কনস্ট্রেইন্ট (NOT NULL, UNIQUE, FK) যোগ করা এবং পরে দেখা যায় লেগ্যাসি ডেটা ভেঙে দেয়।
  • ব্যাকগ্রাউন্ড জব, এক্সপোর্ট, রিপোর্ট ভুলে যাওয়া যা এখনও পুরনো ফিল্ড পড়ে।

একটি সহজ দৃশ্য: আপনি phone-কে mobile_number-এ রিনেম করে দেন, NOT NULL কনস্ট্রেইন্ট যোগ করেন, এবং রিজেনারেট করেন। অ্যাপ স্ক্রিনগুলো কাজ করতে পারে, কিন্তু একটি পুরনো CSV এক্সপোর্ট এখনও phone সিলেক্ট করে, এবং হাজার হাজার বিদ্যমান রেকর্ডে mobile_number null আছে। সাধারণ সমাধান হলো ধাপে পরিবর্তন: নতুন কলাম যোগ করুন, দু'দিকে লেখুন, নিরাপদে ব্যাকফিল করুন, তারপর কনস্ট্রেইন্ট কড়া করে পুরনো কলাম মুছুন—এবং আগে নিশ্চিত করুন কিছুই নির্ভর করছে না।

নিরাপদতর মাইগ্রেশনের জন্য দ্রুত প্রি-ডেপ্লয় চেকলিস্ট

নিরাপদ স্কিমা পরিবর্তন নিয়ে নির্মাণ করুন
আপনার ডাটাবেস ভিজুয়ালি মডেল করে Go ব্যাকএন্ড পুনরায়-জেনারেট করুন, হাতে কোড লিখতে হবে না।
AppMaster ব্যবহার করে দেখুন

আপনার ব্যাকএন্ড রিজেনারেট হলে কোড দ্রুত বদলাতে পারে, কিন্তু প্রোডাকশন ডেটা কীভাবে আচরণ করবে তা ক্ষমাশীল নয়। স্কিমা পরিবর্তন পাঠানোর আগে একটি ছোট “এটা কি নিরাপদে ফেল হতে পারে?” পরীক্ষা চালান। এটি পুনরায়-জেনারেশন-নিরাপদ স্কিমা বিবর্তনকে বোরিং রাখে (এটাই আপনি চান)।

বেশিরভাগ সমস্যা ধরতে এই ৫টি চেক

  • ব্যাকফিল সাইজ ও গতি: কতগুলো বিদ্যমান সারি আপডেট করা লাগবে এবং প্রোডাকশনে কত সময় লাগবে তার অনুমান করুন। ছোট ডাটাবেসে ঠিক থাকা ব্যাকফিল বাস্তবে ঘণ্টার পর ঘণ্টা নেবে এবং অ্যাপকে ধীর করে দিতে পারে।
  • লক ও ডাউনটাইম ঝুঁকি: পরিবর্তনটি লেখাকে ব্লক করতে পারে কি না চিহ্নিত করুন। বড় টেবিলে টাইপ বদলানো বা কিছু আল্টার অপারেশন দীর্ঘকাল লক ধরতে পারে। ব্লকিং সম্ভাবনা থাকলে নিরাপদ রোলআউট পরিকল্পনা করুন (নতুন কলাম আগে, ব্যাকফিল পরে, কোড তারপর)।
  • পুরানো কোড বনাম নতুন স্কিমা সামঞ্জস্য: ধরে নিন পুরানো ব্যাকএন্ড সাময়িকভাবে নতুন স্কিমার বিরুদ্ধে চলতে পারে ডেপ্লয় বা রোলব্যাকের সময়। প্রশ্ন করুন: পূর্ববর্তী ভার্সন crash ছাড়া পড়া/লিখা চালাতে পারবে কি? না হলে দুই-ধাপ রিলিজ দরকার।
  • ডিফল্ট ও নাল আচরণ: নতুন কলামের ক্ষেত্রে বিদ্যমান রেকর্ডে কী হবে সিদ্ধান্ত নিন। সেগুলো NULL হবে, না কি একটি ডিফল্ট দরকার? আপনার লজিক অনুপস্থিত মানগুলোকে স্বাভাবিকভাবে হ্যান্ডেল করছে কি নিশ্চিত করুন—বিশেষত ফ্ল্যাগ, স্ট্যাটাস ও টাইমস্ট্যাম্প ক্ষেত্রে।
  • মনিটরিং সিগন্যালগুলো: ডেপ্লয়ের পর আপনি কোন আলার্মগুলো দেখবেন তা বেছে নিন: এরর রেট (API ফেইল মেসেজ), ডেটাবেস স্লো কুয়েরি, কিউ/জব ফেইল, এবং যেকোনো মূল ব্যবহারকারী কাজ (চেকআউট, লগইন, ফর্ম সাবমিট)। সাইলেন্ট বাগ ধরতে validation error spike-ও দেখুন।

একটি দ্রুত উদাহরণ

আপনি যদি orders টেবিলে একটি required ফিল্ড status যোগ করেন, একবারে "NOT NULL, কোনো ডিফল্ট নেই" অবস্থায় ঠুকবেন না। প্রথমে সেটি nullable হিসেবে যোগ করুন এবং নতুন সারির জন্য একটি নিরাপদ ডিফল্ট দিন, রিজেনারেটেড কোড পাঠান যাতে অনুপস্থিত স্ট্যাটাস হ্যান্ডল করে, তারপর পুরনো সারি ব্যাকফিল করুন, এবং শেষে কনস্ট্রেইন্ট কড়া করুন।

AppMaster-এ এই মাইন্ডসেট বিশেষভাবে উপযোগী কারণ ব্যাকএন্ড ঘনঘন রিজেনারেট হতে পারে। প্রতিটি স্কিমা পরিবর্তনকে একটি ছোট রিলিজ হিসেবে বিবেচনা করুন যার সহজ রোলব্যাক আছে—তাহলে আপনার মাইগ্রেশনগুলো পূর্বানুমানযোগ্য থাকবে।

উদাহরণ: লাইভ অ্যাপকে বিবর্তিত করা בלי বিদ্যমান রেকর্ড ভাঙা

এক্সপ্যান্ড-মাইগ্রেট-কনট্র্যাক্ট ব্যবহার করুন
নতুন ফিল্ড যোগ করুন, ডেটা ব্যাকফিল করুন, এবং নিরাপদভাবে রিড সুইচ করুন—পরিস্কার রোলআউট ধাপ সহ।
এখনই নির্মাণ করুন

ধারণা করুন একটি ইনternalsupport টুল যেখানে এজেন্টরা একটি মুক্ত-টেক্সট priority ফিল্ড দিয়ে টিকিট ট্যাগ করে (উদাহরণ: "high", "urgent", "HIGH", "p1")। আপনি কঠোর enum-এ কনভার্ট করতে চান যাতে রিপোর্ট ও রাউটিং রুল ভুল না করে।

নিরাপদ পন্থা হলো দুই-রিলিজ পরিবর্তন যা পুরনো রেকর্ডগুলোকে বৈধ রাখে যখন ব্যাকএন্ড রিজেনারেট করা হচ্ছে।

রিলিজ 1: expand, দু'দিকে লিখুন, এবং ব্যাকফিল

প্রথমে স্কিমা বিস্তৃত করুন—কিছু মুছে ফেলবেন না। একটি নতুন enum ফিল্ড যোগ করুন, উদাহরণ priority_enum মানগুলো হতে পারে low, medium, high, urgent। মূল priority_text ফিল্ড রাখুন।

তারপর লজিক আপডেট করুন যাতে নতুন ও সম্পাদিত টিকেট দুটো ফিল্ডেই লিখে। AppMaster-এর মতো নো-কোড টুলে সাধারণত Data Designer মডেল এবং বিজনেস প্রসেস আপডেট করে ইনপুটকে enum-এ ম্যাপ করে পাশাপাশি মূল টেক্সটও সংরক্ষণ করবেন।

এখন বিদ্যমান টিকেটগুলো ছোট ব্যাচে ব্যাকফিল করুন। প্রচলিত টেক্সট মানগুলোকে enum-এ ম্যাপ করুন ("p1" এবং "urgent" -> urgent, "HIGH" -> high)। অজানা মানগুলো সাময়িকভাবে medium-এ ম্যাপ করুন এবং পরে রিভিউ করুন।

ইউজারদের যা দেখা যাবে: আদর্শভাবে কিছুই বদলাবে না। UI একই প্রতিপাদন দেখাতে পারে, কিন্তু পিছনে আপনি নতুন enum পূরণ করছেন। রিপোর্ট ব্যাকফিল শুরু হওয়ার সাথে সাথে enum ব্যবহার করতে শুরু করতে পারে।

রিলিজ 2: কনট্র্যাক্ট এবং পুরনো পথ সরান

আপনি কনফিডেন্ট হলে রিডগুলো কেবল priority_enum ব্যবহার করতে দিন, ফিল্টার ও ড্যাশবোর্ড আপডেট করুন, এবং পরে priority_text মুছে ফেলুন।

রিলিজ 2-এর আগে একটি ছোট নমুনায় যাচাই করুন:

  • 20–50 টিকিট বেছে নিন বিভিন্ন টিম এবং ভিন্ন বয়সের।
  • প্রদর্শিত প্রায়োরিটি এবং স্টোর করা enum মান তুলনা করুন।
  • enum মান অনুযায়ী গণনা চেক করুন অস্বাভাবিক স্পাইক আছে কিনা (উদাহরণ খুব বেশি medium)।

যদি সমস্যা দেখা দেয়, রোলব্যাক সহজ কারণ রিলিজ 1 পুরনো ফিল্ড রাখে: রিল.deploy করুন Releasе 1 লজিক এবং UI-কে পুনরায় priority_text পড়ার জন্য সেট করুন যতক্ষণ না ম্যাপিং ঠিক করে পুনরায় ব্যাকফিল করে নেওয়া হয়।

পরবর্তী ধাপ: স্কিমা বিবর্তনকে পুনরাবৃত্ত অভ্যাস বানান

আপনি যদি পূর্বানুমানযোগ্য মাইগ্রেশন চান, তবে স্কিমা পরিবর্তনকে একটি ছোট প্রকল্প হিসেবে বিবেচনা করুন, দ্রুত সম্পাদ্য সম্পাদনা হিসেবে নয়। লক্ষ্য সহজ: প্রতিটি পরিবর্তন ব্যাখ্যা করা সহজ, রিহার্স করা সহজ এবং দুর্ঘটনাক্রমে ভাঙা কঠিন।

ভিজুয়াল ডাটা মডেল সাহায্য করে কারণ এটি ডিপ্লয়ের আগে প্রভাবগুলো দৃশ্যমান করে। যখন আপনি টেবিল, রিলেশন, এবং ফিল্ড টাইপ এক জায়গায় দেখতে পারেন, তখন আপনি স্ক্রিপ্টে সহজে মিস হওয়া জিনিসগুলো খেয়াল করতে পারবেন—যেমন কোনও required ফিল্ড যার নিরাপদ ডিফল্ট নেই, বা এমন একটি রিলেশন যা পুরনো রেকর্ডকে orphan করবে। একটি দ্রুত "কে এর ওপর নির্ভর করে?" পাস করুন: API, স্ক্রীন, রিপোর্ট, এবং যেকোনো ব্যাকগ্রাউন্ড জব।

যখন আপনি একটি ব্যবহৃত ফিল্ড বদলাতে চান, ছোট ট্রানজিশন পিরিয়ড সহ ডুপ্লিকেট ফিল্ড পছন্দ করুন। উদাহরণস্বরূপ, phone_e164 যোগ করুন এবং এক বা দুই রিলিজের জন্য phone_raw রাখুন। ব্যবসায়িক লজিক আপডেট করুন নতুন ফিল্ড থেকে পড়বে যখন উপস্থিত, অন্যথায় পুরনো থেকে fallback নেবে। ট্রানজিশনে দু'দিকে লিখুন, পরে পুরনোটি কেবল তখনই সরান যখন ডেটা সম্পূর্ণভাবে ব্যাকফিল করা আছে তা যাচাই করা হয়েছে।

পরিবেশগত শৃঙ্খলা হলো যা ভাল উদ্দেশ্যকে নিরাপদ রিলিজে পরিণত করে। dev, staging, এবং production-কে সঙ্গত রাখুন, কিন্তু একে অপরের সমান ধরবেন না।

  • Dev: রিজেনারেটেড ব্যাকএন্ড ক্লিনভাবে শুরু হয় এবং বেসিক ফ্লো চলে কিনা প্রমাণ করুন।
  • Staging: production-সদৃশ ডেটার ওপর পুরো মাইগ্রেশন পরিকল্পনা চালান এবং মূল কুয়েরি, রিপোর্ট এবং ইমপোর্ট যাচাই করুন।
  • Production: রোলআউট করুন যখন আপনার রোলব্যাক প্ল্যান, স্পষ্ট মনিটরিং এবং কয়েকটি "মাস্ট পাস" চেক থাকে।

আপনার মাইগ্রেশন পরিকল্পনাকে বাস্তব ডকুমেন্ট বানান, যদিও সংক্ষিপ্তই হোক। এতে থাকুক: কী পরিবর্তিত হবে, অর্ডার, কীভাবে ব্যাকফিল করবেন, কীভাবে যাচাই করবেন, এবং কীভাবে রোলব্যাক করবেন। তারপর এটি প্রোডাকশনে পৌঁছানোর আগে একটি টেস্ট এনভায়রনমেন্টে end-to-end চালান।

আপনি যদি AppMaster ব্যবহার করেন, তাহলে Data Designer-কে ব্যবহার করুন ভিজুয়ালি মডেল বুঝতে, এবং রিজেনারেশনকে আপনার আপডেট স্কিমার সঙ্গে ব্যাকএন্ড কোড সঙ্গত রাখতে দিন। পুনরাবৃত্ত অভ্যাস যা জিনিসগুলো পূর্বানুমানযোগ্য রাখে—মাইগ্রেশনগুলো explicit রাখুন: দ্রুতই ইটারেট করতে পারবেন, কিন্তু প্রতিটি পরিবর্তনের জন্য এখনও বিদ্যমান প্রোডাকশন ডেটার একটি পরিকল্পিত পথ থাকবে।

শুরু করা সহজ
কিছু আশ্চর্যজনকতৈরি করুন

বিনামূল্যের পরিকল্পনা সহ অ্যাপমাস্টারের সাথে পরীক্ষা করুন।
আপনি যখন প্রস্তুত হবেন তখন আপনি সঠিক সদস্যতা বেছে নিতে পারেন৷

এবার শুরু করা যাক