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

বারবারা লিস্কভ এমন একজন কম্পিউটার বিজ্ঞানী যাঁর কাজ নিঃশব্দে আধুনিক সফটওয়্যার টিমগুলোকে এমনভাবে তৈরি করতে প্রভাব ফেলেছে যাতে সেগুলো সহজে ভেঙে না যায়। তাঁর গবেষণা ডেটা অ্যাবস্ট্র্যাকশন, ইনফরমেশন হাইডিং এবং পরে লিস্কভ প্রতিস্থাপন নীতি (LSP) প্রোগ্রামিং ভাষা থেকে শুরু করে প্রতিদিন আমরা API সম্পর্কে যেভাবে ভাবি তা প্রভাবিত করেছে: স্পষ্ট আচরণ সংজ্ঞায়িত করুন, অভ্যন্তরীণ বিষয়গুলো রক্ষিত রাখুন, এবং অন্যরা যেন আপনার ইন্টারফেসে নির্ভর করতে পারে সেটি নিশ্চিত করুন।
একটি বিশ্বাসযোগ্য API কেবল তাত্ত্বিকভাবে “সঠিক” নয়। এটি এমন একটি ইন্টারফেস যা একটি প্রোডাক্টকে দ্রুতগতিতে এগোতে সাহায্য করে:
এই বিশ্বাসযোগ্যতা একটি অভিজ্ঞতা: API কল করা ডেভেলপার, তা রক্ষণাবেক্ষণকারী টিম, এবং যাদের উপর তা পরোক্ষভাবে নির্ভর করে এমন ব্যবহারকারীদের জন্য।
ডেটা অ্যাবস্ট্র্যাকশন হচ্ছে ধারণা যে কলাররা একটি কনসেপ্ট (একটি অ্যাকাউন্ট, একটি কিউ, একটি সাবস্ক্রিপশন) সঙ্গে একটি ছোট সেট অপারেশনের মাধ্যমে ইন্টার্যাক্ট করবে—নৌ কিভাবে তা স্টোর করা বা গণনা করা হচ্ছে তার বিশৃঙ্খলতার মাধ্যমে নয়।
যখন আপনি প্রতিনিধিত্বগত বিশদগুলো লুকিয়ে রাখেন, আপনি ভুলের পুরো ক্যাটাগরি সরিয়ে ফেলেন: কেউই “বিরলভাবে” এমন একটি ডাটাবেস ফিল্ডে নির্ভর করতে পারবে না যা প্রকাশ্য হওয়ার কথা ছিল না, বা সিস্টেম মোকাবেলা করতে না সক্ষম এমনভাবে শেয়ার্ড স্টেটকে মিউটেট করতে পারবে না। ততটাই গুরুত্বপূর্ণ, অ্যাবস্ট্র্যাকশন সমন্বয়ের ওভারহেড কমায়: যতক্ষণ পাবলিক আচরণ একরকম থাকে, টিমগুলোর উচিত নয় অভ্যন্তরীণ রিফ্যাক্টর করার জন্য অনুমতি চাওয়া।
এই লেখার শেষে, আপনার হাতে থাকবে ব্যবহারিক উপায়গুলো:
দ্রুত সারসংক্ষেপ দেখতে এখানে যান: /blog/a-practical-checklist-for-designing-reliable-apis.
ডেটা অ্যাবস্ট্র্যাকশন একটি সহজ ধারণা: আপনি কোনো কিছুর সাথে তার কী করে সেই অনুযায়ী ইন্টারঅ্যাক্ট করেন, কিভাবে তা নির্মিত সেটা নয়।
একটি ভেন্ডিং মেশিন ভাবুন। আপনাকে মোটর কিভাবে ঘোরে বা কয়েন কিভাবে গোনা হয় তা জানা দরকার নেই। কেবল নিয়ন্ত্রণগুলো ("আইটেম নির্বাচন", "পে", "আইটেম পাওয়া") এবং নিয়মগুলো ("যদি আপনি যথেষ্ট পরিশোধ করেন, আপনি আইটেম পাবেন; যদি তা বিক্রি হয়ে যায়, রিফান্ড পাবেন") জানা দরকার। এটিই অ্যাবস্ট্র্যাকশন।
সফটওয়্যারে, ইন্টারফেস হচ্ছে “কি করে”: অপারেশনগুলোর নাম, কোন ইনপুট গ্রহণ করে, কোন আউটপুট দেয়, এবং কোন ত্রুটি আশা করা উচিত। ইমপ্লিমেন্টেশন হচ্ছে “কিভাবে কাজ করে”: ডাটাবেস টেবিল, ক্যাশিং কৌশল, অভ্যন্তরীণ ক্লাস, এবং পারফরম্যান্স ট্রিকস।
এসব আলাদা রেখে আপনি এমন API পাবেন যা সিস্টেম বিকশিত হলেও স্থিতিশীল থাকে। আপনি অভ্যন্তরীন পুনর্লিখন করতে পারেন, লাইব্রেরি বদলাতে পারেন, বা স্টোরেজ অপ্টিমাইজ করতে পারেন—যতক্ষণ ইন্টারফেস ব্যবহারকারীদের জন্য একই থাকে।
একটি অ্যাবস্ট্র্যাক্ট ডেটা টাইপ মানে হচ্ছে “কন্টেইনার + অনুমোদিত অপারেশন + নিয়ম”, নির্দিষ্ট অভ্যন্তরীণ স্ট্রাকচারে অব্দি অনুগত না হয়ে বর্ণনা করা।
উদাহরণ: একটি Stack (last in, first out).
মূল প্রতিশ্রুতি হল: pop() সর্বশেষ push() করা আইটেম প্রদান করে। স্ট্যাক অ্যারে ব্যবহার করে, লিংকড লিস্ট ব্যবহার করে, বা অন্য কিছুও ব্যবহার করুক—সেটা ব্যক্তিগত।
একই পৃথকীকরণ সর্বত্র প্রযোজ্য:
POST /payments হচ্ছে ইন্টারফেস; তত্থ্য ফ্রড চেক, রিট্রাই, এবং ডাটাবেজ লেখাগুলো হচ্ছে ইমপ্লিমেন্টেশন।client.upload(file) হচ্ছে ইন্টারফেস; চাঙ্কিং, কম্প্রেশন, প্যারালাল রিকোয়েস্ট হচ্ছে ইমপ্লিমেন্টেশন।অ্যাবস্ট্র্যাকশনের সাথে ডিজাইন করলে, আপনি যে চুক্তির উপর ব্যবহারকারীরা নির্ভর করে সেটার উপর ফোকাস রাখেন—এবং পর্দার পিছনের সবকিছু বদলালেও তাদের ভাঙতে দেবেন না।
একটি ইনভারিয়েন্ট হচ্ছে এমন একটি নিয়ম যা একটি অ্যাবস্ট্র্যাকশনের ভেতরে সবসময় সত্য থাকা উচিত। API ডিজাইন করলে ইনভ্যারিয়েন্টগুলো হচ্ছে সেই গার্ডরেইল যা আপনার ডেটাকে অসম্ভব অবস্থা থেকে রক্ষা করে—যেমন একটি ব্যাঙ্ক অ্যাকাউন্টে দুইটা ভিন্ন কারেন্সি থাকা, বা একটি “পরিপূর্ণ” অর্ডার যার কোনো আইটেম নেই।
ইনভ্যারিয়েন্টকে ভাবুন আপনার টাইপের “বাস্তবতার আকৃতি” হিসেবে:
Cart-এ নেতিবাচক পরিমাণ থাকতে পারে না।UserEmail সদা বৈধ ইমেইল ঠিকানা (পরে “ভ্যালিডেটেড” নয়)।Reservation-এ start < end, এবং উভয় সময় একই টাইমজোনে।যদি এই বিবৃতিগুলো সত্য না থাকে, আপনার সিস্টেম অনির্বচনীয় হয়ে যায়, কারণ প্রতিটি ফিচার এখন অনুমান করে যে “বিকৃত” ডেটা মানে কী।
ভাল API ইনভ্যারিয়েন্টগুলো বর্ডারেই প্রয়োগ করে:
এটি স্বাভাবিকভাবেই ত্রুটি হ্যান্ডলিং উন্নত করে: অস্পষ্ট ভবিষ্যতের ব্যর্থতার বদলে API বলা যায় কোন নিয়ম ভঙ্গ হয়েছে (“end অবশ্যই start-এর পরে হতে হবে”)।
কলারদেরকে অভ্যন্তরীণ নিয়মগুলো মুখস্থ করে রাখার প্রয়োজন হওয়া উচিত নয়, যেমন “এই মেথডটি কেবল normalize() কল করার পরে কাজ করে।” যদি ইনভ্যারিয়েন্ট কোনো বিশেষ রীতি-অনুশীলনের উপর নির্ভর করে, তাহলে সেটা invariants না—সে একটা ফু্যাটগান।
ইন্টারফেস এমনভাবে ডিজাইন করুন:
API টাইপ ডকুমেন্ট করলে লিখে রাখুন:
ভাল একটি API কেবল ফাংশনের সেট নয়—এটি একটি প্রতিশ্রুতি। কনট্রাক্টগুলো সেই প্রতিশ্রুতি স্পষ্ট করে, যাতে কলাররা আচরণে নির্ভর করতে পারে এবং রক্ষণরক্ষকরা অভ্যন্তরীণ পরিবর্তন করলেও অন্য কাউকে অবাক না করে।
অন্তত নিম্নলিখিতগুলো ডকুমেন্ট করুন:
এই স্পষ্টতা আচরণকে পূর্বানুমানযোগ্য করে তোলে: কলাররা জানবে কোন ইনপুট নিরাপদ এবং কোন আউটকামগুলো হ্যান্ডেল করতে হবে, এবং টেস্টগুলো উদ্দেশ্য যাচাই করতে পারবে।
কনট্রাক্ট না থাকলে টিমগুলো স্মৃতি ও অনানুষ্ঠানিক রীতি উপর নির্ভর করে: “সেখানে null পাস করবেন না”, “ওকল কলটি মাঝে মাঝে রিট্রাই করে”, “এরর হলে খালি রিটার্ন করে”। এসব নিয়ম অনবোর্ডিং, রিফ্যাক্টর বা ঘটনাকালীন হারিয়ে যায়।
একটি লিখিত কনট্রাক্ট সেই লুকানো নিয়মগুলোকে শেয়ার্ড জ্ঞানে পরিণত করে। এটি কোড রিভিউয়ের জন্যও স্থিতিশীল লক্ষ্য তৈরি করে: আলোচনা হবে “এ পরিবর্তন কি এখনো কনট্রাক্ট মেনে চলে?” পরিবর্তে “এটা আমার কাছে কাজ করেছে”।
অস্পষ্ট: “Creates a user.”
ভালো: “Creates a user with a unique email.
email must be a valid address; caller must have users:create permission.userId; the user is persisted and immediately retrievable.409 if email already exists; returns 400 for invalid fields; no partial user is created.”অস্পষ্ট: “Gets items quickly.”
ভাল: “Returns up to limit items sorted by createdAt descending.
nextCursor for the next page; cursors expire after 15 minutes.”ইনফরমেশন হাইডিং হচ্ছে ডেটা অ্যাবস্ট্র্যাকশনের ব্যবহারিক পাশ: কলাররা API কী করে তাতে নির্ভর করবে, কিভাবে করে তাতে না। যদি ব্যবহারকারীরা আপনার ইন্টার্নাল দেখতে না পায়, আপনি সেগুলো বদলে দিতে পারবেন প্রতিটি রিলিজ ব্রেকিং করে না।
একটি ভাল ইন্টারফেস একটি ছোট অপারেশন সেট প্রকাশ করে (create, fetch, update, list, validate) এবং প্রতিনিধিত্ব—টেবিল, ক্যাশ, কিউ, ফাইল লেআউট, সার্ভিস বাউন্ডারি—প্রাইভেট রাখে।
উদাহরণ: “add item to cart” একটি অপারেশন; আপনার ডাটাবেসের CartRowId একটি ইমপ্লিমেন্টেশন ডিটেইল। যখন আপনি ডিটেইল প্রকাশ করেন, ব্যবহারকারীরা তার উপর নিজেদের লজিক বানাবেন, যা আপনার বদলানোর ক্ষমতাকে জবরদস্তি করে।
যখন ক্লায়েন্টরা কেবল স্থিতিশীল আচরণে নির্ভর করে, আপনি করতে পারবেন:
এবং API সামঞ্জস্যপূর্ণ থাকবে কারণ চুক্তি নড়েনি। এটিই প্রকৃত রিটার্ন: ব্যবহারকারীদের জন্য স্থিতিশীলতা, রক্ষণকারীদের জন্য স্বাধীনতা।
ইনটার্নাল আকস্মিকভাবে বেরিয়ে আসার কিছু উপায়:
status=3 গ্রহণ করা বরাবর স্পষ্ট নাম বা ডেডিকেটেড অপারেশন ব্যবহার না করে।অর্থ নয়, কৌশল ব্যাখ্যা করে এমন রেসপন্সগুলো প্রাধান্য দিন:
"userId": "usr_…") ডাটাবেস রো-নম্বরের বদলে।কোনো ডিটেইল বদলাতে পারে বলে চিন্তা হলে, সেটি প্রকাশ করবেন না। যদি ব্যবহারকারীরা সত্যিই সেটির প্রয়োজন অনুভব করে, তা deliberateভাবে ইন্টারফেসের অংশ হিসেবে প্রচার করুন ও ডকুমেন্ট করুন।
লিস্কভ প্রতিস্থাপন নীতির এক বাক্যে: যদি কোনো কোড একটি ইন্টারফেস দিয়ে কাজ করে, তখন সেটি যে কোন বৈধ ইমপ্লিমেন্টেশন প্রতিস্থাপন করলেও কাজ চালিয়ে যাবে—কোনো বিশেষ কেস ছাড়া।
LSP ইনহেরিটেন্স সম্পর্কে কম এবং বিশ্বাস সম্পর্কে বেশি। যখন আপনি একটি ইন্টারফেস প্রকাশ করেন, আপনি আচরণ সম্পর্কে একটি প্রতিশ্রুতি দিচ্ছেন। LSP বলে প্রতিটি ইমপ্লিমেন্টেশনই সেই প্রতিশ্রুতি রাখবে, এমনকি এটি সম্পূর্ণ ভিন্ন অভ্যন্তরীণ পন্থা ব্যবহার করুক।
কলাররা আপনার API যা বলে তার উপর নির্ভর করে—এবং আজ যা করে তা উপর নির্ভর করে না। যদি একটি ইন্টারফেস বলে “আপনি যে কোন বৈধ রেকর্ড দিয়ে save() কল করতে পারেন,” তাহলে প্রতিটি ইমপ্লিমেন্টেশন সেই বৈধ রেকর্ডগুলি গ্রহণ করবে। যদি ইন্টারফেস বলে "get() একটি মান বা স্পষ্ট ‘not found’ ফলাফল দেয়", তাহলে ইমপ্লিমেন্টেশন হঠাৎ করে নতুন ত্রুটি ছুঁড়ে দিবে বা আংশিক ডেটা ফিরিয়ে দেবে না।
নিরাপদ এক্সটেনশন মানে আপনি নতুন ইমপ্লিমেন্টেশন যোগ করতে বা প্রোভাইডার বদলে দিতে পারবেন ক্লায়েন্টদের কোড পুনরায় লেখার বাধ্যবাধকতা ছাড়াই। এটাই LSP-এর ব্যবহারিক লাভ: ইন্টারফেসগুলো সহজে প্রতিস্থাপনযোগ্য রাখা।
API চুক্তি ভঙ্গের দুইটি সাধারণ উপায়:
কঠোরতর ইনপুট (তীক্ষ্ণ প্রিকন্ডিশন): নতুন ইমপ্লিমেন্টেশন এমন ইনপুট প্রত্যাখ্যান করে যেগুলো ইন্টারফেস ডেফাইনেশন অনুমোদন করেছিল। উদাহরণ: বেস ইন্টারফেস যে কোনো UTF‑8 স্ট্রিং আইডি হিসেবে গ্রহণ করে, কিন্তু একটি ইমপ্লিমেন্টেশন শুধুমাত্র নমেরিক আইডি গ্রহণ করে বা খালি কিন্তু বৈধ ফিল্ডগুলো প্রত্যাখ্যান করে।
দুর্বল আউটপুট (কমপ্লিটনেস কমানো): নতুন ইমপ্লিমেন্টেশন প্রতিশ্রুতিটির তুলনায় কম ফেরত দেয়। উদাহরণ: ইন্টারফেস বলে ফলাফলগুলো সাজানো, ইউনিক বা সম্পূর্ণ—কিন্তু একটি ইমপ্লিমেন্টেশন অসাজানো ডেটা, ডুপ্লিকেট বা আইটেম গোপন করে ফেরত দেয়।
একটি তৃতীয় সূক্ষ্ম লঙ্ঘন হচ্ছে ব্যর্থতার আচরণ পরিবর্তন: একটি ইমপ্লিমেন্টেশন “not found” ফেরত দেয়, অন্যটি একই পরিস্থিতিতে এক্সসেপশন ফেলে—এতে কলাররা নিরাপদে প্রতিস্থাপন করতে পারে না।
প্লাগ-ইন সমর্থন করতে ইন্টারফেসকে চুক্তির মতো লিখুন:
যদি কোনো ইমপ্লিমেন্টেশন বাস্তবে কঠোরতর নিয়ম দাবি করে, একই ইন্টারফেসের নিচে তা লুকাবেন না। (1) আলাদা ইন্টারফেস ডিফাইন করুন, অথবা (2) স্পষ্ট ক্যাপাবিলিটি হিসেবে প্রকাশ করুন—উদাহরণ: supportsNumericIds()—তাহলে ক্লায়েন্ট জ্ঞানভিত্তিকভাবে অপশনে সাইন-আপ করবে, ব্লাইন্ড সাবস্টিটিউশন নয়।
একটি ভাল ডিজাইনকৃত ইন্টারফেস “ব্যবহার করা সহজ” মনে হয় কারণ এটি কেবল কলারের যা দরকার তা প্রকাশ করে—আর তার বেশি নয়। লিস্কভের ডেটা অ্যাবস্ট্র্যাকশন দর্শন আপনাকে সংকীর্ণ, স্থিতিশীল এবং পাঠযোগ্য ইন্টারফেসের দিকে ঠেলে দেয়, যাতে ব্যবহারকারীরা অভ্যন্তরীণ বিবরণ না জেনে নির্ভর করতে পারে।
বিশাল API গুলো প্রায়ই অপ্রাসঙ্গিক দায়িত্ব মিশিয়ে দেয়: কনফিগারেশন, স্টেট চেঞ্জ, রিপোর্টিং ও ট্রাবলশুটিং এক জায়গায়। এতে বোঝা কঠিন হয়ে যায় কখন কি কল করা নিরাপদ।
একটি সংহত ইন্টারফেস একই অ্যাবস্ট্র্যাকশনের অপারেশনগুলোকে গ্রুপ করে। আপনার API যদি একটি কিউ উপস্থাপন করে, তাহলে কেবল কিউ আচরণগুলিই ফোকাস করুন (enqueue/dequeue/peek/size), সাধারণ ইউটিলিটি নয়। কম কনসেপ্ট = কম ভুল ব্যবহারের পথ।
“নমনীয়” প্রায়ই “অসম্পষ্ট” অর্থে আসে। options: any, mode: string, বা একাধিক বুলিয়ান (force, skipCache, silent) এমন কম্বিনেশন তৈরি করে যা স্পষ্টভাবে সংজ্ঞায়িত নয়।
পছন্দ করুন:
publish() বনাম publishDraft()), অথবাযদি কোনো প্যারামিটার জানার জন্য কলারকে সোর্স পড়তে হয় তাহলে সেটি একটি ভাল অ্যাবস্ট্র্যাকশনের অংশ নয়।
নামগুলো চুক্তি সংবেদনশীল করে। পর্যবেক্ষণযোগ্য আচরণ বর্ণনা করে এমন ক্রিয়াপদ বেছে নিন: reserve, release, validate, list, get। মজার রূপক বা ওভারলোডেড টার্ম এড়ান। যদি দুটি মেথডই শোনাতে মিলছে, ব্যবহারকারীরা ধরে নেবেন সেগুলো একইরকম—তাই তা নিশ্চিত করুন।
এগুলো আলাদা করুন যখন দেখতে পাবেন:
অ্যালাদা মডিউল আপনাকে অভ্যন্তরীণভাবে বিকাশ করতে দেয় যখন মূল প্রতিশ্রুতি স্থিতিশীল রাখা হয়। যদি বৃদ্ধির পরিকল্পনা থাকে, একটি পাতলা “কোর” প্যাকেজ প্লাস অ্যাড-অন বিবেচনা করুন; দেখুন /blog/evolving-apis-without-breaking-users।
API বিরলভাবে স্থির থাকে। নতুন ফিচার আসে, এজকেস আবিষ্কৃত হয়, এবং “ছোট উন্নতি” আস্তে আস্তে বাস্তব অ্যাপ্লিকেশন ভাঙতে পারে। লক্ষ্যটি ইন্টারফেস আটকে রাখা নয়—বরং এটি ব্যবহারকারীদের উপর নির্ভর করা চুক্তি ভঙ্গ না করে বিকশিত করা।
সেমান্টিক ভার্সনিং একটি যোগাযোগের উপায়:
সীমা: বিচারবোধ এখনও প্রয়োজন। একটি “বাগ ফিক্স” যদি এমন আচরণ বদলায় যাতে কলাররা নির্ভর করেছিল, তাহলে তা বাস্তবে ব্রেকিং—এটা সংখ্যা নীতিতে ধরবে না।
অনেক ব্রেকিং পরিবর্তন কম্পাইলারের বাইরে ঘটে:
চিন্তা করুন preconditions ও postconditions এর দিক থেকে: কলাররা কি দিতে হবে, এবং তারা কি ফিরে পাবেন—এসবের উপর।
ডিপ্রিকেশন কাজ করে যখন তা স্পষ্ট ও টাইম-বাউন্ড:
লিস্কভ-স্টাইল ডেটা অ্যাবস্ট্র্যাকশন সাহায্য করে কারণ এটি সীমিত করে কী উপর ব্যবহারকারীরা নির্ভর করতে পারে। যদি কলাররা কেবল ইন্টারফেস চুক্তিতে নির্ভর করে—না অভ্যন্তরীণ স্ট্রাকচারে—আপনি স্টোরেজ ফরম্যাট, অ্যালগরিদম, অপ্টিমাইজেশন স্বাধীনভাবে পরিবর্তন করতে পারবেন।
প্রায়োগিকভাবে, শক্ত টুলিংও সহায়ক। উদাহরণস্বরূপ, আপনি যদি দ্রুত অভ্যন্তরীণ API-তে ইটারেট করছেন একটি React ওয়েব অ্যাপ বা Go + PostgreSQL ব্যাকএন্ড নির্মাণের সময়, একটি চ্যাট-চালিত বিল্ডার जैसे Koder.ai দ্রুত ইমপ্লিমেন্টেশনে সহায়তা করতে পারে—তবে মূল শৃঙ্খলা অপরিবর্তিত থাকে: পরিষ্কার কনট্রাক্ট, স্থিতিশীল আইডেন্টিফায়ার, ব্যাকওয়ার্ড-কম্প্যাটিবল ইভলিউশান। গতি একটি গুণনকারী—তাই সঠিক ইন্টারফেস অভ্যাসগুলোকে গুণুন।”
তিনি ডেটা অ্যাবস্ট্র্যাকশন ও ইনফরমেশন হাইডিংকে জনপ্রিয় করেছেন, যা আধুনিক API ডিজাইনের সরাসরি অনুবাদ: একটি ছোট, স্থিতিশীল চুক্তি প্রকাশ করুন এবং ইমপ্লিমেন্টেশনকে নমনীয় রাখুন। ফলাফল প্রায়োগিক: কম ব্রেকিং পরিবর্তন, নিরাপদ রিফ্যাক্টর, ও অধিক পূর্বনির্ধারিত ইন্টিগ্রেশন।
একটি বিশ্বাসযোগ্য API এমন যে কলাররা সময়ের সাথে নির্ভর করতে পারে:
বিশ্বাসযোগ্যতা মানে “কখনও ব্যর্থ না হওয়া” নয় — বরং বিফল হলে পূর্বানুমানযোগ্যভাবে এবং চুক্তি রক্ষা করা।
একটি চুক্তি হিসেবে আচরণ লিখুন:
শূন্য ফল, ডুপ্লিকেট, অর্ডারিং—এসব এজ কেসগুলোও অন্তর্ভুক্ত করুন যাতে কলাররা প্রমিস অনুযায়ী ইমপ্লিমেন্ট ও টেস্ট করতে পারে।
ইনভ্যারিয়েন্ট হলো একটি নিয়ম যা একটি অ্যাবস্ট্র্যাকশনের ভেতরে সর্বদা সত্য থাকা উচিত (যেমন “পরিমাণ কখনই ঋণাত্মক হবে না”)। API-কে নীচের স্থায়ী সীমায় ইনভ্যারিয়েন্ট সঞ্চালন করা উচিত:
এভাবে downstream ত্রুটি কমে, কারণ সিস্টেম আর “অসম্ভব” স্টেট নিয়ে হ্যান্ডেল করতে হয় না।
ইনফরমেশন হাইডিং মানে প্রকাশ করা অপারেশন ও মানে, নয় ইমপ্লিমেন্টেশনের উপস্থাপনা। কনসিউমারকে এমন জিনিসে নির্ভর করা থেকে বিরত করুন যা আপনি ভবিষ্যতে বদলাতে পারেন (টেবিল, ক্যাশ, শার্ড কী, ইন্টারনাল স্ট্যাটাস)।
প্রায়োগিক কৌশল:
usr_...) ডেটাবেজ রো-আইডি নয়।কারণ ক্লায়েন্ট যখন ডাটাবেস-আকৃতির ফিল্টার, জয়েন কী বা ইনটের্নাল আইডির উপর নির্ভর করে, তখন আপনার ইমপ্লিমেন্টেশন ‘ফ্রোজেন’ হয়ে যায়। স্কিমা পরিবর্তন করা মানে API ভাঙানো হয়ে পরে।
এখানে সিদ্ধান্ত: স্টোরেজকে গোপন রাখুন এবং ক্লায়েন্টকে ডোমেইন-ভিত্তিক প্রশ্ন করতে দিন—যেমন “এক জন কাস্টমারের অর্ডার নির্দিষ্ট তারিখ সীমায়” বলেই বলুন, কিভাবে ডেটা স্টোর করা হয়েছে তা বলবেন না।
LSP মানে: যদি কোড কোনো ইন্টারফেসের সঙ্গে কাজ করে, সেটি অবশ্যই ইন্টারফেসের যেকোনো বৈধ ইমপ্লিমেন্টেশন বদলে দিলেও কাজ করবে—কোনো বিশেষ কেস ছাড়াই। API-র পরিপ্রেক্ষিতে: “কলারকে অবাক করে দিও না”।
প্রতিস্থাপনযোগ্য ইনপ্লিমেন্টেশন সহ করতে, স্ট্যান্ডার্ডাইজ করুন:
মনোযোগ রাখুন:
যদি কোনো ইমপ্লিমেন্টেশনের কড়া শর্ত সত্যিই জরুরি হয়, আলাদা ইন্টারফেস বা স্পষ্ট ক্যাপাবিলিটি ফ্ল্যাগ দিন যেন ক্লায়েন্ট সচেতনভাবে সাইন-আপ করে।
নির্ভরযোগ্য ইন্টারফেস ছোট, সংহত ও পড়তে সহজ হওয়া উচিত:
options: any বা অনেক বুলিয়ান প্যারামিটার এড়ান; অস্পষ্টতা বাড়ে।reserve, release, , )।ভুল-উদ্দেশ্যভিত্তিক ব্যর্থতা (প্রোগ্রামার এরর) এবং রানটাইম ব্যর্থতা আলাদা করুন:
একই কনট্রাক্ট অনুযায়ী ব্যর্থির ধরন নির্ধারণ করুন: স্থিতিশীল ত্রুটি কোড ও স্ট্রাকচার্ড ফিল্ড দিন যেন টেস্টগুলো মেসেজ স্ট্রিংয়ের উপর নির্ভর না করে।
status=3listvalidateযদি ভিন্ন ভূমিকা বা ভিন্ন পরিবর্তন হারের অংশ থাকে, মডিউল আলাদা করুন (আরও পড়ুন: /blog/evolving-apis-without-breaking-users)।