২৬ অক্টো, ২০২৫·6 মিনিট

প্রোটোটাইপগুলোকে কম ঝুঁকিতে মডিউলে রিফ্যাক্টর করা

পর্যায়ভিত্তিক পরিকল্পনায় প্রোটোটাইপকে মডিউলে রিফ্যাক্টর করুন—প্রতিটি পরিবর্তন ছোট, টেস্টযোগ্য এবং দ্রুত উল্টানো যায় এমন করে রাখুন; রুটস, সার্ভিস, DB এবং UI জুড়ে ঝুঁকি কমান।

প্রোটোটাইপগুলোকে কম ঝুঁকিতে মডিউলে রিফ্যাক্টর করা

কেন প্রোটোটাইপ পরিবর্তন করা ঝুঁকিপূর্ণ হয়ে ওঠে

প্রোটোটাইপ দ্রুত লাগে কারণ সবকিছু কাছে মিলে থাকে। একটি রুট ডাটাবেসে পৌঁছে, রেসপন্স গঠন করে, এবং UI তা রেন্ডার করে। সেই গতি বাস্তব, কিন্তু এতে একটি খরচ লুকানো থাকে: যখন আরো ফিচার আসে, প্রথম "দ্রুত পথ" হয়ে ওঠে সবকিছুর নির্ভরতার পথ।

প্রথমে যা ভাঙে তা সাধারণত নতুন কোড নয়। তা হলো পুরনো অনুমানগুলো।

রুটে একটি ছোট পরিবর্তন চুপচাপ রেসপন্সের আকার পরিবর্তন করে দুইটা স্ক্রিন ভাঙিয়ে দিতে পারে। একাধিক স্থানে কপি করা একটি "অস্থায়ী" কুয়েরি সামান্য ভিন্ন ডেটা ফিরিয়ে আনতে শুরু করে, এবং কেউ জানে না কোনটা সঠিক।

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

কম-ঝুঁকিপূর্ণ রিফ্যাক্টরিং মানে পরিবর্তনগুলো ছোট ও উল্টানো যায় এমন রাখা। আপনি যে কোনও ধাপে থামতে পারবেন এবং এখনও কাজ করে এমন অ্যাপ পাবেন। ব্যবহারিক নিয়মগুলো সহজ:

  • প্রতিটি পরিবর্তনের জন্য এক স্পষ্ট লক্ষ্য (কোড সরান, যা করে তা বদলাবেন না)
  • ছোট সাইকেল যেখানে প্রতিবার একই ব্যবহারকারীর ফ্লো পুনরায় পরীক্ষা করা যায়
  • দ্রুত কার্যকর করা যায় এমন এক বিরক্তিকর রোলব্যাক পরিকল্পনা
  • নতুন কোড পুরনো কোডের পাশে থাকবে যতক্ষণ না তা নিজেকে প্রমাণ করে

রুটস, সার্ভিস, ডাটাবেস অ্যাক্সেস, এবং UI তখন জটিল হয় যখন প্রতিটি স্তর অন্যদের কাজ করাও শুরু করে। জটিলতা কাটানো মানে "পারফেক্ট আর্কিটেকচার" অনুসরণ করা নয়। এটা একবারে এক থ্রেড সরানোর ব্যাপার।

কোড স্পর্শ করার আগে নিয়ম ঠিক করুন

রিফ্যাক্টরিংকে একটি রিমুভ করার মতো বিবেচনা করুন, রেনোভেশন নয়। আচরণ একই রাখুন, এবং স্ট্রাকচারকে পরে পরিবর্তন করা সহজ করুন। আপনি যদি পুনর্গঠনের সময়ই ফিচারও "উন্নত" করেন, তাহলে আপনি যা ভাঙল এবং কেন তা ট্র্যাক করতে পারবেন না।

লিখে রাখুন কোনগুলো এখনো বদলাবে না। সাধারণ "এখনো নয়" আইটেম: নতুন ফিচার, UI রিডিজাইন, ডাটাবেস স্কিমা পরিবর্তন, এবং পারফরম্যান্স কাজ। এই সীমানা কাজকে কম-ঝুঁকিপূর্ণ রাখে।

একটি "গোল্ডেন পাথ" ব্যবহারকারীর ফ্লো বেছে নিন এবং সেটি রক্ষা করুন। এমন কিছু বাছাই করুন যা মানুষ দৈনন্দিন করে, উদাহরণ:

sign in -> create item -> view list -> edit item -> save

আপনি প্রতিটি ছোট ধাপের পরে এই ফ্লোটি পুনরায় চালাবেন। যদি এটি একইভাবে আচরণ করে, আপনি এগিয়ে যেতে পারবেন।

প্রথম কমিটের আগে রোলব্যাক নিয়ে একমত হোন। রোলব্যাক হওয়া উচিত বিরক্তিকর: একটি git revert, একটি স্বল্প-আয়ু ফিচার ফ্ল্যাগ, অথবা একটি প্ল্যাটফর্ম স্ন্যাপশট যা পুনরুদ্ধার করা যায়। যদি আপনি Koder.ai ব্যবহার করে তৈরি করছেন, স্ন্যাপশট এবং রোলব্যাক পুনর্গঠনের সময় একটি সহায়ক নিরাপত্তা জাল হতে পারে।

প্রতিটি স্তরের জন্য ছোট একটি ডিফিনিশন অফ ডান রাখুন। আপনার বড় চেকলিস্ট প্রয়োজন নেই, শুধু এতটুকু যাতে "সরান + পরিবর্তন" চুরি করে না:

  • একই ইনপুট একই আউটপুট তৈরি করে (এরোর এবং খালি স্টেট সহ)
  • গোল্ডেন পাথ দ্রুত ম্যানুয়াল টেস্ট পাস করে (বা একটি ছোট অটোমেটেড চেক)
  • পরিবর্তনগুলো স্টেজের সীমার মধ্যে থাকে (শুধু রুটস, বা শুধু সার্ভিস)
  • রোলব্যাক অন্তত একবার যাচাই করা হয়েছে (আপনি সত্যিই এটি চেষ্টা করেন)
  • একটি ছোট নোট ব্যাখ্যা করে কী সরানো হয়েছে এবং এখন কোথায় আছে

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

দ্রুত ইনভেন্টরি: রুটস, স্ক্রিন, এবং ডেটা পথ

শুরু করার আগে, আজ যা আছে তা ম্যাপ করুন। এটা কোনো রিডিজাইন নয়। এটি একটি নিরাপত্তা ধাপ যাতে আপনি ছোট, উল্টানো-যুগ্য পদক্ষেপ নিতে পারেন।

প্রতিটি রুট বা এন্ডপয়েন্ট তালিকাভুক্ত করুন এবং এটি কী করে তা এক বাক্যে লিখুন। UI রুট (পেজ) এবং API রুট (হ্যান্ডলার) উভয়ই অন্তর্ভুক্ত করুন। যদি আপনি একটি চ্যাট-চালিত জেনারেটর ব্যবহার করে এবং কোড এক্সপোর্ট করে থাকেন, এটি একইভাবে আচরণ করুন: ইনভেন্টরিটি যা ব্যবহারকারী দেখে তাও কোড যা স্পর্শ করে তা মেলে।

একটি হালকা-ওজন ইনভেন্টরি যা ব্যবহারযোগ্য থাকবে:

  • Route/endpoint + উদ্দেশ্য (উদাহরণ: "/checkout" পেমেন্ট ফর্ম দেখায়)
  • UI screen/component যা এটিকে ট্রিগার করে
  • ব্যবসায়িক নিয়মগুলো (প্রাইসিং, ভ্যালিডেশন, পারমিশন)
  • ডাটাবেস টেবিলগুলো এবং অ্যাক্সেসের ধরন (read/write/transaction)
  • কপি এবং পুনরাবৃত্তি (একই ভ্যালিডেশন তিনটি ফাইলে কপি করা)

প্রতি রুটের জন্য একটি দ্রুত "ডেটা পাথ" নোট লিখুন:

UI event -> handler -> logic -> DB query -> response -> UI update

যেতে যেতে, ঝুঁকিপূর্ণ এলাকাগুলো ট্যাগ করুন যেন আপনি আশেপাশের কোড পরিষ্কার করার সময় ভুলবশত সেগুলো বদলান না:

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

শেষে, একটি সাধারণ টার্গেট মডিউল ম্যাপ স্কেচ করুন। এটাকে গভীর রাখবেন না। আপনি গন্তব্য নির্বাচন করছেন, নতুন সিস্টেম তৈরি করছেন না:

routes/handlers, services, db (queries/repositories), ui (screens/components)

যদি আপনি বলতে না পারেন একটি কোডের টুকরা কোথায় থাকা উচিত, তাহলে সেই এলাকাটি ভাল একটি প্রার্থী পরবর্তীতে রিফ্যাক্টর করার জন্য, যখন আপনি বেশি আত্মবিশ্বাস পাবেন।

স্টেজ 1: লজিক না বদলে রুটগুলো স্থিতিশীল করুন

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

প্রতি ফিচার এরিয়ার জন্য একটি পাতলা মডিউল তৈরি করুন, যেমন users, orders, বা billing। "সরানোর সময় পরিষ্কার করা" এড়িয়ে চলুন। যদি আপনি একই কমিটে নাম পরিবর্তন, ফাইল পুনর্বিন্যস্ত এবং লজিক পুনরায় লিখেন, তাহলে কি ভাঙেছে তা দেখা কঠিন হবে।

একটি নিরাপদ ক্রম:

  • বিদ্যমান রুট হ্যান্ডলারগুলো ফিচার অনুযায়ী গ্রুপ করুন, এমনকি যদি অভ্যন্তরীন অংশ বিশৃঙ্খল হয়।
  • প্রথমে শুধু वायरিং কোডই সরান: প্যারামস পড়া, বেসিক ভ্যালিডেশন, একই বিদ্যমান ফাংশনগুলো কল করা।
  • রেসপন্সগুলো একেবারে একই রাখুন: স্ট্যাটাস কোড, এরর মেসেজ, এবং পেলোড আকৃতি।
  • প্রতিটি রুটের জন্য একটি ছোট গোল্ডেন-পাথ চেক যোগ করুন (একটি হ্যাপি-পাথ কল যা কাজ করে)।

কনক্রিট উদাহরণ: যদি আপনার একটি একক ফাইল থাকে যার মধ্যে POST /orders JSON পার্স করে, ফিল্ড চেক করে, টোটাল হিসাব করে, ডাটাবেসে লেখে, এবং নতুন অর্ডার রিটার্ন করে, তা পুনরায় লিখবেন না। হ্যান্ডলারটিকে orders/routes এ বের করে পুরনো লজিককে কল করুন, উদাহরণস্বরূপ createOrderLegacy(req)। নতুন রুট মডিউলটি সামনের দরজা হবে; লিগ্যাসি লজিক অস্থায়ীভাবে অপরিবর্তিত থাকবে।

আপনি যদি জেনারেটেড কোডের সঙ্গে কাজ করে থাকেন (উদাহরণ: Koder.ai তে উৎপন্ন Go ব্যাকএন্ড), মানসিকতা বদলায় না। প্রতিটি এন্ডপয়েন্টকে অনুমানযোগ্য স্থানে রাখুন, লিগ্যাসি লজিককে র‍্যাপ করুন, এবং প্রচলিত রিকোয়েস্ট এখনও সফল হয় কিনা প্রমাণ করুন।

স্টেজ 2: ব্যবসায়িক লজিক সার্ভিসে টানুন

Centralize database access
Move SQL into named functions and keep transactions consistent across the app.

রুটস ব্যবসায়িক নিয়মের জন্য ভাল জায়গা নয়। সেগুলো দ্রুত বাড়ে, কনসার্ন মিশিয়ে ফেলে, এবং প্রতিটি পরিবর্তন ঝুঁকিপূর্ণ দেখায় কারণ আপনি একসাথে সবকিছু স্পর্শ করেন।

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

সার্ভিস ফাংশনগুলো সহজে বোঝার যোগ্য থাকবে যখন তাদের একটি কাজ থাকবে, স্পষ্ট ইনপুট থাকবে, এবং স্পষ্ট আউটপুট থাকবে। যদি আপনি বারবার "এবং আরো..." যোগ করে যাচ্ছেন, তা ভাগ করুন।

একটি নামকরণ প্যাটার্ন সাধারণত কাজ করে:

  • CreateOrder(input) -> order
  • CancelOrder(orderId, actor) -> result
  • GetOrderSummary(orderId) -> summary

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

অগ্রসর হওয়ার আগে, পরিবর্তনগুলো উল্টানো যোগ্য করতে যথেষ্ট টেস্ট যোগ করুন:

  • একটি হ্যাপি-পাথ টেস্ট যা সঠিকভাবে তৈরি/আপডেট করে
  • একটি এরর-পাথ টেস্ট (প্রয়োজনীয় ফিল্ড অনুপস্থিত, অনুমতি অস্বীকার)

আপনি যদি দ্রুত জেনারেট বা ইটারেট করতে Koder.ai মত টুল ব্যবহার করেন, সার্ভিসগুলো আপনার এনকার হবে। রুট এবং UI পরিবর্তিত হতে পারে, কিন্তু নিয়মগুলো স্থিতিশীল ও টেস্টযোগ্য থাকবে।

স্টেজ 3: ডাটাবেস অ্যাক্সেস নিরাপদভাবে বিচ্ছিন্ন করুন

একবার রুটগুলো স্থিতিশীল এবং সার্ভিস তৈরি হয়ে গেলে, ডাটাবেসকে "প্রতিটি জায়গায়" থাকা বন্ধ করুন। কাঁচা কুয়েরিগুলোকে একটি ছোট, বিরক্তিকর ডেটা অ্যাক্সেস লেয়ারের পিছনে লুকান।

একটি ক্ষুদ্র মডিউল তৈরি করুন (repository/store/queries) যা স্পষ্ট নামযুক্ত কয়েকটি ফাংশন প্রকাশ করে, যেমন GetUserByEmail, ListInvoicesForAccount, বা SaveOrder। এখানেই অন্বেষণ করবেন না। প্রতিটি SQL স্ট্রিং বা ORM কলের জন্য একটি স্পষ্ট বাড়ি লক্ষ্য করুন।

এই স্টেজটি কঠিনভাবে স্ট্রাকচার সম্পর্কিত রাখুন। স্কিমা পরিবর্তন, ইনডেক্স টুইক, বা "এই সময়ে" মাইগ্রেশন এড়িয়ে চলুন। এদের জন্য আলাদা পরিকল্পিত পরিবর্তন ও রোলব্যাক প্রয়োজন।

ট্রানজ্যাকশনগুলো এক জায়গায় রাখুন

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

বরং, একটি এন্ট্রি পয়েন্ট তৈরি করুন যা একটি কলব্যাককে ট্রানজ্যাকশনের ভিতরে চালায়, এবং রেপোসিটরিগুলোকে ট্রানজ্যাকশন কনটেক্সট গ্রহণ করতে দিন।

চালনা ছোট রাখুন:

  • একবারে একটি কুয়েরি রেপোসিটরিতে সরান, একই ইনপুট/আউটপুট রেখে।
  • সার্ভিসগুলো সরাসরি নতুন রেপো ফাংশন কল করুক (অতিরিক্ত লজিক না রেখে)।
  • ট্রানজ্যাকশন begin/commit/rollback এক হেল্পারে কেন্দ্রীভূত করুন।
  • এরর ম্যাপিং স্ট্যান্ডার্ড করুন (উদাহরণ: "not found" বনাম "unexpected error")।

উদাহরণস্বরূপ, যদি "Create Project" একটি প্রজেক্ট ইনসার্ট করে এবং তারপর ডিফল্ট সেটিংস ইনসার্ট করে, উভয় কলকে একটি ট্রানজ্যাকশন হেল্পারের ভিতরে মোড়ান। কিছু মাঝামাঝি ব্যর্থ হলে, আপনার কাছে এমন প্রজেক্ট থাকবে না যার সেটিংস নেই।

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

স্টেজ 4: UI কম্পোনেন্টগুলো পরিষ্কার করুন, রিডিজাইন না করে

UI পরিষ্কার করা মানে সুন্দর করা নয়। এটি স্ক্রিনগুলোকে পূর্বানুমানযোগ্য করা এবং আশ্চর্যজনক সাইড-ইফেক্ট কমানো।

ফিচার অনুসারে UI কোড গ্রুপ করুন, تکنিক্যাল টাইপ অনুযায়ী নয়। একটি ফিচার ফোল্ডার তার স্ক্রিন, ছোট কম্পোনেন্ট, এবং লোকাল হেল্পার রাখতে পারে। যদি আপনি একই মার্কআপ বারবার দেখেন (একই বাটন রো, কার্ড, বা ফর্ম ফিল্ড), তা এক্সট্র্যাক্ট করুন, কিন্তু মার্কআপ ও স্টাইলিং একই রাখুন।

প্রপসগুলো সরল রাখুন। কেবল যা একটি কম্পোনেন্টের দরকার তা পাঠান (স্ট্রিং, আইডি, বুলিয়ান, কলব্যাক)। যদি আপনি একটি বড় অবজেক্ট "জানতে চাইলে" পাঠাচ্ছেন, একটি ছোট শেপ নির্ধারণ করুন।

UI কম্পোনেন্টগুলো থেকে API কল সরান। সার্ভিস লেয়ার থাকা সত্ত্বেও, UI কোড প্রায়ই ফেচ লজিক, রিট্রাই, এবং মানচিত্রকরণ ধারণ করে। প্রতিটি ফিচারের (বা API এরিয়ার) জন্য একটি ছোট ক্লায়েন্ট মডিউল তৈরি করুন যা স্ক্রিনের জন্য প্রস্তুত-আইটেম ডেটা ফিরিয়ে দেয়।

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

প্রতিটি এক্সট্র্যাকশনের পরে, আপনি টাচ করা স্ক্রিনটির দ্রুত ভিজ্যুয়াল চেক করুন। প্রধান অ্যাকশনগুলো ক্লিক করুন, পেজ রিফ্রেশ করুন, এবং একটি এরর কেস ট্রিগার করুন। ছোট ধাপ বড় UI রিরাইট ছাড়িয়ে জয় করে।

বাস্তবসম্মত উদাহরণ: একটি ফিচার সম্পূর্ণরূপে রিফ্যাক্টর করা

Clean up UI without redesign
Extract components and move API calls out of screens while keeping the same UI.

কল্পনা করুন একটি ছোট প্রোটোটাইপ যার তিনটি স্ক্রিন আছে: সাইন ইন, আইটেম তালিকা, আইটেম এডিট। এটি কাজ করে, কিন্তু প্রতিটি রুট অথ চেক, ব্যবসায়িক নিয়ম, SQL, এবং UI স্টেটে মিশ্রিত। লক্ষ্য হলো কেবল এই ফিচারটিকে একটি পরিষ্কার মডিউলে পরিণত করা, এমন পরিবর্তন করে যা আপনি রোলব্যাক করতে পারবেন।

আগে এবং পরে: কী কোথায় সরানো হয়

আগে, "items" লজিক ছড়িয়ে থাকতে পারে:

server/
  main.go
  routes.go
  handlers.go          # sign in + items + random helpers
  db.go                # raw SQL helpers used everywhere
web/
  pages/
    SignIn.tsx
    Items.tsx          # fetch + state + form markup mixed

পরে, আচরণ একই থাকে, কিন্তু সীমানাগুলো স্পষ্ট:

server/
  routes/
    items_routes.go
  handlers/
    items_handler.go
  services/
    items_service.go
  repositories/
    items_repo.go
web/
  pages/
    Items.tsx
  components/
    ItemForm.tsx

একটি end-to-end পাস (ছোট, উল্টানো যোগ্য)

প্রথমে লজিক না বদলে কোড সরান। ফাংশন নাম এবং রিটার্ন শেপ একই রাখুন, তাই রোলব্যাক মূলত ফাইল মুভ হবে।

একটি রুট প্রথম রিফ্যাক্টর করুন। হ্যান্ডলারটি শুধুমাত্র HTTP দিয়ে কাজ করা উচিত, না নিয়ম বা SQL দিয়ে:

// handlers/items_handler.go
func (h *ItemsHandler) UpdateItem(w http.ResponseWriter, r *http.Request) {
  userID := h.auth.UserIDFromRequest(r) // same auth logic as before
  itemID := mux.Vars(r)["id"]

  var in UpdateItemInput
  json.NewDecoder(r.Body).Decode(&in)

  item, err := h.itemsService.UpdateItem(r.Context(), userID, itemID, in)
  if err != nil { writeError(w, err); return }
  writeJSON(w, item)
}

কিন্তু এরপর ব্যবসায়িক নিয়ম সার্ভিসে নিয়ে যান। এখানে আপনি যেমন "ব্যবহারকারী শুধুই তাদের নিজস্ব আইটেম এডিট করতে পারবে" বা "নাম খালি হতে পারে না" চেক রাখবেন। সার্ভিস রেপোকে DB কাজের জন্য কল করে:

// services/items_service.go
func (s *ItemsService) UpdateItem(ctx context.Context, userID, itemID string, in UpdateItemInput) (Item, error) {
  if strings.TrimSpace(in.Name) == "" { return Item{}, ErrInvalidName }
  return s.repo.UpdateItem(ctx, userID, itemID, in)
}

তারপর DB অ্যাক্সেস একটি রেপোসিটরিতে বিচ্ছিন্ন করুন:

// repositories/items_repo.go
func (r *ItemsRepo) UpdateItem(ctx context.Context, userID, itemID string, in UpdateItemInput) (Item, error) {
  row := r.db.QueryRowContext(ctx,
    `UPDATE items SET name=$1 WHERE id=$2 AND user_id=$3 RETURNING id, name`,
    in.Name, itemID, userID,
  )
  var it Item
  err := row.Scan(&it.ID, &it.Name)
  return it, err
}

UI দিকে, পেজ লেআউট রাখা, কিন্তু পুনরাবৃত্ত ফর্ম মার্কআপ একটি শেয়ার্ড কম্পোনেন্টে বের করুন যা "নিউ" এবং "এডিট" দুই ফ্লোই ব্যবহার করে:

  • pages/Items.tsx ফেচিং এবং নেভিগেশন রাখে
  • components/ItemForm.tsx ইনপুট ফিল্ড, ভ্যালিডেশন মেসেজ, এবং সাবমিট বাটন নিয়ন্ত্রণ করে

আপনি যদি Koder.ai (koder.ai) ব্যবহার করে থাকেন, তার সোর্স কোড এক্সপোর্ট ডিপার রিফ্যাক্টরের আগে একটি সহায়ক রেফারেন্স পয়েন্ট হতে পারে, এবং স্ন্যাপশট/রোলব্যাক দ্রুত পুনরুদ্ধার করতে সাহায্য করতে পারে যখন কোনো মুভ ভাঙে।

সাধারণ ফাঁদ যা ঝুঁকি বাড়ায়

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

আরেকটি ফাঁদ হলো ক্লিনআপ যা আচরণ বদলায়। ভ্যারিয়েবলের নাম পরিবর্তন ঠিক আছে; ধারণার নাম বদলানো নয়। যদি status স্ট্রিং থেকে সংখ্যায় পরিবর্তিত হয়, আপনি কেবল কোডই নয় পণ্য পরিবর্তন করছেন। তা পরে স্পষ্ট টেস্ট এবং পরিকল্পিত রিলিজের মাধ্যমে করুন।

নিদান যা চুপচাপ সমস্যা তৈরী করে

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

আরও লক্ষ্য রাখুন UI যদি সরাসরি ডাটাবেসে পৌছায় (বা একটি হেল্পারের মাধ্যমে কাঁচা কুয়েরি কল করে)। এটি দ্রুত লাগে, কিন্তু প্রতিটি স্ক্রিনকে পারমিশন, ডেটা নিয়ম, এবং এরর হ্যান্ডলিং এর দায়িত্ব দেয়।

ঝুঁকি বাড়ান এমনগুলো এড়ান:

  • বড় রিফ্যাক্টর কমিট যা অনেক ফাইল স্পর্শ করে এবং একই সঙ্গে লজিক বদলে
  • শেয়ার্ড রিটার্ন শেপ পরিবর্তিত হওয়া কিন্তু প্রতিটি কলার আপডেট না হওয়া
  • নতুন লেয়ার যুক্ত করা কিন্তু স্পষ্ট কারণ না থাকা
  • এরর হ্যান্ডলিং ফ্ল্যাট করা (সবকিছু null বা একটি সাধারণ মেসেজ হয়ে যাওয়া)

একটি ছোট উদাহরণ: যদি একটি স্ক্রিন { ok: true, data } আশা করে কিন্তু নতুন সার্ভিস { data } রিটার্ন করে এবং এররগুলিতে থ্রো করে, তাহলে অ্যাপের অর্ধেকই বন্ধুত্বপূর্ণ মেসেজ দেখানো বন্ধ করে দিতে পারে। প্রথমে boundary-তে পুরনো শেপ রাখুন, পরে কলারগুলো একে একে মাইগ্রেট করুন।

পরবর্তী স্টেজে যাওয়ার আগে দ্রুত চেক

Generate a service per action
Describe one user action and scaffold a handler and service with clear inputs and outputs.

পরবর্তী ধাপে যাওয়ার আগে, প্রমাণ করুন আপনি প্রধান অভিজ্ঞতা ভাঙেননি। প্রতিবার একই গোল্ডেন পাথ রান করুন (সাইন ইন, আইটেম তৈরি, দেখা, সম্পাদনা, মুছে ফেলা)। ধারাবাহিকতা ছোট রিগ্রেশনগুলো ধরতে সাহায্য করে।

প্রতিটি স্টেজের পরে একটি সহজ go/no-go গেট ব্যবহার করুন:

  • গোল্ডেন পাথ এখনও end-to-end কাজ করে, সাধারণ এরর কেসসহ।
  • রুটস/কন্ট্রোলার পাতলা: ইনপুট পড়ে, একটি সার্ভিস কল করে, রেসপন্স ফেরত দেয়।
  • সার্ভিসগুলো পোর্টেবল: রুট, ব্যাকগ্রাউন্ড জব, বা UI অ্যাকশন থেকে কল করা যায় HTTP না জেনে।
  • ডাটাবেস অ্যাক্সেস কেন্দ্রিভূত যেখানে একটি ট্রানজ্যাকশন প্যাটার্ন এবং সঙ্গতিপূর্ণ এরর হ্যান্ডলিং আছে।
  • রোলব্যাক দ্রুত এবং প্রমাণিত।

যদি কোনো একটি ফেল করে, থামুন এবং ঠিক করুন আগে আরও উপরে নির্মাণ করার। ছোট ফাটলগুলো পরে বড় ফাটলে পরিণত হয়।

একটি দ্রুত “আমরা কি এটা উল্টিয়ে দিতে পারি?” ড্রিল

মার্জ করার ঠিক পরে, পাঁচ মিনিট ব্যয় করে যাচাই করুন আপনি ব্যাক আউট করতে পারবেন:

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

পরবর্তী পদক্ষেপ: প্রোডাক্ট বাড়ার সঙ্গে মডিউলার রাখা

জয় প্রথম ক্লিনআপ নয়। জয় হলো আকার বজায় রাখা যেমন আপনি ফিচার যোগ করেন। আপনি পারফেক্ট আর্কিটেকচার পিংছে করছেন না। আপনি ভবিষ্যৎ পরিবর্তনগুলো পূর্বানুমানযোগ্য, ছোট, এবং সহজে উল্টানো যোগ্য করছে।

পরবর্তী মডিউল বেছে নিন প্রভাব এবং ঝুঁকি দেখে, না যে কি বিরক্ত করছে। ভাল টার্গেট হচ্ছে ব্যবহারকারীরা যা প্রায় স্পর্শ করে, যেখানে আচরণ আগেই বোঝা গেছে। অস্পষ্ট বা ভঙ্গুর এলাকাগুলো রেখে দিন যতক্ষণ না আপনার কাছে ভালো টেস্ট বা পণ্য উত্তর আছে।

সহজ কেডেন্স রাখুন: একটি জিনিস সরানো একটি ছোট PR, দ্রুত রিভিউ সাইকেল, ঘন রিলিজ, এবং একটি স্টপ-লাইন নিয়ম (যদি স্কোপ বাড়ে, তা ভাগ করুন এবং ছোট অংশ পাঠান)।

প্রতিটি স্টেজের আগে, একটি রোলব্যাক পয়েন্ট সেট করুন: একটি git ট্যাগ, একটি রিলিজ ব্রাঞ্চ, বা একটি ডিপ্লয়েবল বিল্ড যা আপনি জানেন কাজ করে। যদি আপনি Koder.ai তে তৈরি করেন, Planning Mode আপনাকে পরিবর্তনগুলো ধাপে ধাপে সাজাতে সাহায্য করতে পারে যাতে আপনি অসাবধানতায় তিনটি লেয়ার একসঙ্গে রিফ্যাক্টর না করেন।

মডিউলার অ্যাপ আর্কিটেকচারের জন্য ব্যবহারিক নিয়ম: প্রতিটি নতুন ফিচার একই সীমানা অনুসরণ করে। রুটস পাতলা থাকে, সার্ভিস ব্যবসায়িক নিয়ম রাখে, ডাটাবেস কোড এক জায়গায় থাকে, এবং UI কম্পোনেন্ট ডিসপ্লেতে ফোকাস করে। যখন কোনো নতুন ফিচার সেই নিয়মগুলো ভাঙে, ছোট অবস্থায় রিফ্যাক্টর করুন।

সাধারণ প্রশ্ন

Why does a prototype get risky to change even if it still “works”?

Default: treat it as risk. Even small response-shape changes can break multiple screens.

Do this instead:

  • Freeze behavior first (same status codes, payload shape, error messages)
  • Protect one “golden path” flow and re-test it after every small move
  • Keep changes reversible (small commits, verified rollback)
  • Move code first, then improve code in a later, separate change
What’s a good “golden path” to protect while refactoring?

Pick a flow people do daily and that touches the core layers (auth, routes, DB, UI).

A good default is:

  • Sign in → create item → view list → edit item → save (and optionally delete)

Keep it small enough to run repeatedly. Add one common failure case too (e.g., missing required field) so you notice error-handling regressions early.

What’s the simplest rollback plan that actually works?

Use a rollback you can execute in minutes.

Practical options:

  • Revert the last commit (or a small set of commits)
  • A short-lived feature flag/config switch that restores the old code path
  • A platform snapshot/rollback if your environment supports it

Verify rollback once early (actually do it), so it’s not a theoretical plan.

In what order should I refactor: routes, services, DB access, or UI?

A safe default order is:

  1. Routes/handlers: move endpoints into predictable modules, keep logic the same
  2. Services: pull business rules out of routes into one function per action
  3. Database layer: move queries into repositories/stores and standardize transactions
  4. UI: extract reusable components and move API calls out of screens

This order reduces blast radius: each layer becomes a clearer boundary before you touch the next one.

How do I refactor without accidentally changing behavior?

Make “move” and “change” two separate tasks.

Rules that help:

  • One goal per change (relocate code without changing outputs)
  • Keep request/response shapes identical at the boundary
  • Avoid “while we’re here” work (schema tweaks, perf changes, UI redesign)

If you must change behavior, do it later with clear tests and a deliberate release.

Can I do this safely with generated code (like code produced by a chat tool)?

Yes—treat it like any other legacy codebase.

A practical approach:

  • Wrap existing logic with thin handlers in new locations (e.g., CreateOrderLegacy)
  • Prove the same requests still succeed (golden path + one error case)
  • Only after it’s stable, extract services and repositories behind that wrapper

Generated code can be reorganized safely as long as you keep the external behavior consistent.

How do I handle transactions without breaking things?

Centralize transactions and make them boring.

Default pattern:

  • One helper starts/commits/rolls back a transaction
  • Repositories accept a transaction/context instead of opening their own
  • Services decide when a transaction is needed; repositories just run queries

This prevents partial writes (e.g., creating a record without its dependent settings) and makes failures easier to reason about.

What’s the minimum testing that still makes refactoring safer?

Start with just enough coverage to make changes reversible.

Minimum useful set:

  • One happy-path test per key service action (create/update)
  • One error-path test (missing field, permission denied)
  • A quick manual run of the golden path after each small change

You’re aiming to reduce fear, not to build a perfect test suite overnight.

How do I clean up UI components without triggering a redesign?

Keep layout and styling the same at first; focus on predictability.

Safe UI cleanup steps:

  • Group by feature (screen + components + helpers together)
  • Extract repeated markup into small components without redesign
  • Move API calls into a small client module; screens should orchestrate, not fetch
  • Standardize loading, error, and empty states across screens

After each extraction, do a quick visual check and trigger one error case.

How can Koder.ai features help me keep refactors low-risk?

Use platform safety features to keep changes small and recoverable.

Practical defaults:

  • Use snapshots/rollback before and during risky file moves
  • Export source code before deeper refactors if you want a stable reference point
  • Use a planning step to define scope boundaries (what will not change yet)
  • Deploy in small increments so you can stop after any step with a working app

These habits support the main goal: small, reversible refactors with steady confidence.

Related posts

কর্মী অফবোর্ডিং অ্যাপ: নিরাপদে অ্যাক্সেসের ফাঁক বন্ধ করুন

এমন একটি কর্মী অফবোর্ডিং অ্যাপের পরিকল্পনা করুন, যা ফেরত দেওয়ার কাজ ভাগ করে, সরঞ্জামের অবস্থা রেকর্ড করে এবং HR, ম্যানেজার ও IT-এর অনুমোদন সংগ্রহ করে।

কর্মীরা ব্যবহারের আগে ব্যবসায়িক অ্যাপের জন্য বাস্তবসম্মত টেস্ট ডেটা

ব্যবসায়িক অ্যাপের জন্য বাস্তবসম্মত টেস্ট ডেটা তৈরি, কর্মীদের অনুমতি মডেল, জটিল পরিস্থিতি পরীক্ষা এবং লঞ্চের আগে ভুল ইনপুট শনাক্ত করার উপায় জানুন।

শিফট কর্মীদের জন্য অ্যাপ: শেয়ার করা ডিভাইস ডিজাইনের টিপস

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