Go API ত্রুটি হ্যান্ডলিং প্যাটার্ন যা টাইপ করা ত্রুটি, HTTP স্ট্যাটাস কোড, রিকোয়েস্ট আইডি এবং নিরাপদ বার্তা একরূপ করে — অভ্যন্তরীণ তথ্য ফাঁস করা ছাড়াই।

যখন প্রতিটি এন্ডপয়েন্ট ভিন্নভাবে ব্যর্থতা জানায়, ক্লায়েন্টরা আপনার API‑র উপর বিশ্বাস হারায়। এক রুট ফিরিয়ে দেয় { "error": "not found" }, অন্যটি দেয় { "message": "missing" }, আর তৃতীয়টি পাঠায় প্লেইন টেক্সট। যদিও অর্থ মিলতে পারে, ক্লায়েন্ট কোড এখন জুডশ করতে বাধ্য হয় কী ঘটেছে।
ব্যয় দ্রুত প্রকাশ পায়। টিমগুলো ভাঙা পার্সিং লজিক তৈরি করে এবং প্রতি এন্ডপয়েন্টে বিশেষ কেস যোগ করে। রিট্রাই ঝুঁকিপূর্ণ হয়ে যায় কারণ ক্লায়েন্ট বুঝতে পারে না “পুনরায় চেষ্টা করুন” কি “আপনার ইনপুট ভুল”। সাপোর্ট টিকিট বেড়ে যায় কারণ ক্লায়েন্ট কেবল অস্পষ্ট একটি বার্তা দেখে এবং আপনার দল সহজে সার্ভার‑সাইড লগ লাইনের সাথে মিলাতে পারে না।
একটি সাধারণ দৃশ্য: মোবাইল অ্যাপ সাইনআপের সময় তিনটি শেষপয়েন্ট কল করে। প্রথমটি HTTP 400 দিবে একটি ফিল্ড-লেভেল ত্রুটি ম্যাপ সহ, দ্বিতীয়টি HTTP 500 দিবে স্ট্যাক ট্রেস স্ট্রিং, এবং তৃতীয়টি HTTP 200 দিয়ে { "ok": false } পাঠাবে। অ্যাপ টিম তিনটা আলাদা ত্রুটি হ্যান্ডলার পাঠায়, এবং আপনার ব্যাকএন্ড টিম এখনও পায় রিপোর্ট “signup কখনও কখনও ব্যর্থ হয়” — কোথা থেকে শুরু করবেন বোঝা যায় না।
লক্ষ্য একটি পূর্বানুমানযোগ্য চুক্তি। ক্লায়েন্টদের নির্ভরযোগ্যভাবে পড়তে পারা উচিত কী ঘটেছে — সেটা তাদের ভুল নাকি আপনার, পুনরায় চেষ্টা করা উচিত কি না, আর একটি request ID যাতে তারা সাপোর্টে পেস্ট করতে পারে।
স্কোপ নোট: এখানে JSON HTTP API‑র উপর ফোকাস করা হয়েছে (gRPC নয়), কিন্তু একই ধারণা যেখানেই আপনি সিস্টেমকে ত্রুটি ফিরান সেখানে প্রযোজ্য।
একটি স্পষ্ট চুক্তি বেছে নিন এবং প্রতিটি এন্ডপয়েন্টকে তা মানতে বাধ্য করুন। “একরকম” মানে একই JSON আকার, ক্ষেত্রগুলোর একই অর্থ, এবং একই আচরণ — কোন হ্যান্ডলার ব্যর্থ হোক না কেন। একবার এটি করলে, ক্লায়েন্টরা অনুমান করা বন্ধ করে এবং সত্যিকার হ্যান্ডলিং করতে পারে।
একটি দরকারি চুক্তি ক্লায়েন্টকে পরের পদক্ষেপ ঠিক করতে সাহায্য করে। বেশিরভাগ অ্যাপে, প্রতিটি ত্রুটি রেসপন্সকে তিনটি প্রশ্নের উত্তর দিতে হবে:
প্রায়োগিক নিয়মাবলি:
আগে থেকেই সিদ্ধান্ত নিন কোন জিনিসগুলো কখনো রেসপন্সে দেখানো যাবে না। সাধারণ “কখনো না” আইটেমগুলোর মধ্যে থাকতে পারে SQL ফ্র্যাগমেন্ট, স্ট্যাক ট্রেস, অভ্যন্তরীণ হোস্টনেম, সিক্রেট, এবং ডিপেন্ডেন্সি‑থেকে আসা কাঁচা ত্রুটি স্ট্রিং।
একটি পরিষ্কার বিভাজন রাখুন: সংক্ষিপ্ত ব্যবহারকারী‑দৃষ্টিকোণ বার্তা (নিরাপদ, ভদ্র, কার্যনির্দেশক) এবং অভ্যন্তরীণ বিবরণ (পূর্ণ ত্রুটি, স্ট্যাক, কনটেক্সট) শুধু লগে রাখুন। উদাহরণস্বরূপ, “Could not save your changes. Please try again.” নিরাপদ। “pq: duplicate key value violates unique constraint users_email_key” নয়।
যখন সব এন্ডপয়েন্ট একই চুক্তি মেনে চলে, ক্লায়েন্ট একটিমাত্র ত্রুটি হ্যান্ডলার বানাতে পারে এবং সেটি সর্বত্র ব্যবহার করতে পারে।
ক্লায়েন্টরা কেবল তখনই ত্রুটিগুলো পরিষ্কারভাবে হ্যান্ডল করতে পারবে যখন প্রতিটি এন্ডপয়েন্ট একই আকারে উত্তর দেবে। একটি JSON এনভেলপ বেছে নিন এবং তা স্থিতিশীল রাখুন।
একটি ব্যবহারিক ডিফল্ট হলো একটি error অবজেক্ট প্লাস একটি শীর্ষ‑স্তরের request_id:
{
"error": {
"code": "VALIDATION_FAILED",
"message": "Some fields are invalid.",
"details": {
"fields": {
"email": "must be a valid email address"
}
}
},
"request_id": "req_01HV..."
}
HTTP স্ট্যাটাস বিস্তৃত ক্যাটাগরি দেয় (400, 401, 409, 500)। যান্ত্রিকভাবে পড়ার মতো error.code ক্লায়েন্টকে নির্দিষ্ট কেসে শাখা করার সুযোগ দেয়। এই বিভাজন গুরুত্বপূর্ণ কারণ অনেক ভিন্ন সমস্যা একই স্ট্যাটাস শেয়ার করতে পারে। মোবাইল অ্যাপ EMAIL_TAKEN বনাম WEAK_PASSWORD‑এর জন্য ভিন্ন UI দেখাতে পারে, যদিও উভয়ই 400 হতে পারে।
error.message‑কে নিরাপদ ও মানুষ-পঠনীয় রাখুন। এটি ব্যবহারকারীকে সমস্যা ঠিক করতে সাহায্য করবে, কিন্তু অভ্যন্তরীণ তথ্য (SQL, স্ট্যাক ট্রেস, প্রোভাইডার নাম, ফাইল পাথ) কখনো ফাঁস করবে না।
ঐচ্ছিক ক্ষেত্রগুলি তখনই উপকারী যদি সেগুলো পূর্বানুমানযোগ্য থাকে:
details.fields — ফিল্ড থেকে মেসেজের ম্যাপ।details.retry_after_seconds।details.docs_hint সাধারণ টেক্সট হিসেবে (URL নয়)।ব্যাকওয়ার্ড কম্প্যাটিবিলিটির জন্য, error.code‑কে আপনার API চুক্তির অংশ হিসেবে বিবেচনা করুন। নতুন কোড যোগ করুন কিন্তু পুরনো মানে বদলাবেন না। শুধুমাত্র ঐচ্ছিক ক্ষেত্র যোগ করুন, এবং ধরুন ক্লায়েন্ট অচেনা ক্ষেত্রগুলো উপেক্ষা করবে।
ত্রুটি হ্যান্ডলিং ঝামেলার মধ্যে পড়ে যখন প্রতিটি হ্যান্ডলার ব্যর্থতা জানাতে নিজের উপায় উদ্ভাবন করে। একটি ছোট সেট টাইপ করা ত্রুটি এই সমস্যা ঠিক করে: হ্যান্ডলারগুলো জানাবে পরিচিত ত্রুটি টাইপ, এবং একটি একক রেসপন্স লেয়ার সেগুলোকে একরকম রেসপন্সে রূপান্তর করবে।
একটি ব্যবহারিক শুরু সেট বেশিরভাগ এন্ডপয়েন্ট কভার করে:
কীটি হল শীর্ষ পর্যায়ে স্থিতিশীলতা, এমনকি মূল কারণ বদলালেও। আপনি নিম্ন‑স্তরের ত্রুটি (SQL, নেটওয়ার্ক, JSON পার্সিং) মোড়াতে পারেন, তবুও একই পাবলিক টাইপ রিটার্ন করতে পারবেন যেটা মিডলওয়্যার শনাক্ত করে।
type NotFoundError struct {
Resource string
ID string
Err error // private cause
}
func (e NotFoundError) Error() string { return "not found" }
func (e NotFoundError) Unwrap() error { return e.Err }
আপনার হ্যান্ডলারে, sql.ErrNoRows সরাসরি লিক না করে NotFoundError{Resource: "user", ID: id, Err: err} রিটার্ন করুন।
ত্রুটি চেক করার জন্য errors.As ব্যবহার করুন কাস্টম টাইপগুলির জন্য এবং errors.Is ব্যবহার করুন সেন্টিনেল ত্রুটির জন্য। সেন্টিনেল ত্রুটি (যেমন var ErrUnauthorized = errors.New("unauthorized")) সরল কেসে কাজ করে, কিন্তু কাস্টম টাইপগুলো জিতবে যখন আপনাকে নিরাপদ কনটেক্সট (যেমন কোন রিসোর্সটি মিসিং ছিল) প্রয়োজন হবে — তা আপনার পাবলিক রেসপন্স চুক্তি বদলায় না।
সংযুক্ত করার বিষয়ে কড়া থাকুন:
Err, স্ট্যাক ইনফো, কাঁচা SQL ত্রুটি, টোকেন, ব্যবহারকারীর ডাটা।এই বিভাজন আপনাকে ক্লায়েন্টকে সাহায্য করতে দেয় অভ্যন্তরীণ তথ্য প্রকাশ না করে।
একবার আপনার কাছে টাইপ করা ত্রুটি থাকলে, পরবর্তী কাজ হলো দরকারি কিন্তু নীরস: একই ত্রুটি টাইপ সবসময় একই HTTP স্ট্যাটাস উৎপন্ন করবে। ক্লায়েন্টরা এর উপর লজিক তৈরি করবে।
একটি ব্যবহারিক ম্যাপিং যা বেশিরভাগ API‑র জন্য কাজ করে:
| Error type (example) | Status | When to use it |
|---|---|---|
| BadRequest (malformed JSON, missing required query param) | 400 | The request is not valid at a basic protocol or format level. |
| Unauthenticated (no/invalid token) | 401 | The client needs to authenticate. |
| Forbidden (no permission) | 403 | Auth is valid, but access is not allowed. |
| NotFound (resource ID does not exist) | 404 | The requested resource is not there (or you choose to hide existence). |
| Conflict (unique constraint, version mismatch) | 409 | The request is well-formed, but it clashes with current state. |
| ValidationFailed (field rules) | 422 | The shape is fine, but business validation fails (email format, min length). |
| RateLimited | 429 | Too many requests in a time window. |
| Internal (unknown error) | 500 | Bug or unexpected failure. |
| Unavailable (dependency down, timeout, maintenance) | 503 | Temporary server-side issue. |
দুইটি পার্থক্য যা অনেক বিভ্রান্তি রোধ করে:
রিট্রাই নির্দেশনা গুরুত্বপূর্ণ:
একটি রিকোয়েস্ট আইডি হল ছোট একটি ইউনিক মান যা এক API কলের end-to-end পরিচয় দেয়। ক্লায়েন্টরা যদি প্রতিটি রেসপন্সে এটি দেখতে পায়, সাপোর্ট সহজ হয়ে যায়: “আমাকে রিকোয়েস্ট আইডিটি পাঠান” প্রায়ই যথেষ্ট হয় সঠিক লগ এবং ব্যর্থতা খুঁজে পেতে।
এই অভ্যাস সফল এবং ত্রুটি উভয় রেসপন্সেই উপকার দেয়।
একটি স্পষ্ট নিয়ম ব্যবহার করুন: ক্লায়েন্ট যদি রিকোয়েস্ট আইডি পাঠায়, রাখুন। না পাঠালে নতুন তৈরি করুন।
X-Request-Id).রিকোয়েস্ট আইডি তিন জায়গায় রাখুন:
request_id হিসাবে)ব্যাচ এন্ডপয়েন্ট বা ব্যাকগ্রাউন্ড জবগুলোর জন্য একটি প্যারেন্ট রিকোয়েস্ট আইডি রাখুন। উদাহরণ: ক্লায়েন্ট 200 সারি আপলোড করে, 12টি ভ্যালিডেশন ফেইল করে, এবং আপনি কাজ এনকিউ করেন। পুরো কলের জন্য একটি request_id রিটার্ন করুন, এবং প্রতিটি জব বা প্রতিটি আইটেম ত্রুটিতে parent_request_id যোগ করুন। এভাবে আপনি একবারের আপলোডকে ট্রেস করতে পারবেন এমনকি যখন এটি বহু টাসকে ফ্যান‑আউট করে।
ক্লায়েন্টদের একটি স্পষ্ট, স্থির ত্রুটি রেসপন্স দরকার। আপনার লগগুলোকে দরকার messy‑তথ্য দরকার। সেই দুইটির মাঝে বিচ্ছিন্নতা রাখুন: ক্লায়েন্টকে নিরাপদ বার্তা ও পাবলিক কোড দিন, সার্ভারে অভ্যন্তরীণ কারণ, স্ট্যাক, ও কনটেক্সট লগ করুন।
প্রতি ত্রুটি রেসপন্সের জন্য একটি স্ট্রাকচার্ড ইভেন্ট লগ করুন, সেটা request_id দিয়ে সার্চ করা যাবে।
যেগুলো নিয়মিত রাখতে চান:
অভ্যন্তরীণ বিবরণ শুধু সার্ভার লগে (বা একটি অভ্যন্তরীণ ত্রুটি স্টোরে) রাখুন। ক্লায়েন্ট কখনোই কাঁচা ডাটাবেস ত্রুটি, কুয়েরি টেক্সট, স্ট্যাক ট্রেস বা প্রোভাইডার মেসেজ দেখবে না। যদি আপনার বহু সার্ভিস থাকে, একটি অভ্যন্তরীণ ফিল্ড যেমন source (api, db, auth, upstream) ট্রায়াজ দ্রুত করতে সাহায্য করে।
নয়েজি এন্ডপয়েন্ট ও রেট‑লিমিট ত্রুটিগুলোর দিকে নজর রাখুন। যদি কোনো এন্ডপয়েন্ট হাজার হাজার বার প্রতি মিনিটে একই 429 বা 400 তৈরি করে, লগ স্প্যাম থেকে বিরত থাকবার জন্য ইভেন্টগুলো স্যাম্পল করুন, অথবা প্রত্যাশিত ত্রুটির ক্ষেত্রে সেভিরিটি লো করে দিন—তবুও মেট্রিক্সে তাদের কাউন্ট রাখুন।
মেট্রিক্স সমস্যা লগের থেকে আগে ধরে ফেলে। HTTP স্ট্যাটাস এবং ত্রুটি কোড গ্রুপ করে কাউন্ট ট্র্যাক করুন, এবং হঠাৎ স্পাইক‑এ অ্যালার্ট দিন। যদি RATE_LIMITED ডিপ্লয়মেন্টের পরে 10x বৃদ্ধি পায়, আপনি সেটি দ্রুত দেখবেন যদিও লগ স্যাম্পল করা হয়েছে।
ত্রুটিগুলো সামঞ্জস্যপূর্ণ করার সহজতম উপায় হলো সেগুলোকে "সব জায়গায়" হ্যান্ডল করা বন্ধ করে ছোট একটি পাইপলাইনের মাধ্যমে রুট করা। সেই পাইপলাইন ঠিক করে দেয় ক্লায়েন্ট কী দেখবে এবং কী আপনি লগে রাখবেন।
একটি ছোট সেট ত্রুটি কোড দিয়ে শুরু করুন যা ক্লায়েন্ট নির্ভর করতে পারে (উদাহরণ: INVALID_ARGUMENT, NOT_FOUND, UNAUTHORIZED, CONFLICT, INTERNAL)। সেগুলোকে টাইপ করা ত্রুটিতে মোড়ান যেগুলো শুধুমাত্র নিরাপদ, পাবলিক ফিল্ড উন্মুক্ত করে (code, safe message, ঐচ্ছিক details)। অভ্যন্তরীণ কারণ গোপন রাখুন।
এরপর একটি ট্রান্সলেটর ফাংশন তৈরি করুন যা যেকোনো ত্রুটিকে (statusCode, responseBody)‑তে বদলে দেয়। এখানেই টাইপ করা ত্রুটিগুলো HTTP স্ট্যাটাসে ম্যাপ হয়, এবং অজানা ত্রুটি নিরাপদ 500 রেসপন্সে পরিণত হয়।
পরবর্তী, মিডলওয়্যার যোগ করুন যা:
request_id আছেএকটি panic কখনো ক্লায়েন্টকে স্ট্যাক ট্রেস ডাম্প করা উচিত নয়। সাধারণ একটি 500 রেসপন্স রিটার্ন করুন একটি জেনেরিক মেসেজসহ, এবং একই request_id দিয়ে পুরো panic‑টি সম্পূর্ণভাবে লগ করুন।
শেষে, আপনার হ্যান্ডলারেরা রেসপন্স লিখার পরিবর্তে error রিটার্ন করবে। একটি র্যাপার হ্যান্ডলার হোক যা হ্যান্ডলার কল করে, ট্রান্সলেটর চালায়, এবং স্ট্যান্ডার্ড ফর্ম্যাটে JSON লিখে।
একটি সংক্ষিপ্ত চেকলিস্ট:
গোল্ডেন টেস্ট গুরুত্বপূর্ণ কারণ সেগুলো চুক্তি লক করে। কেউ পরে যদি মেসেজ বা স্ট্যাটাস কোড বদলে দেয়, টেস্ট ফেল করবে ক্লায়েন্টদের অবাক হওয়ার আগে।
ধরা যাক একটি এন্ডপয়েন্ট: ক্লায়েন্ট একটি কাস্টমার রেকর্ড তৈরি করে।
POST /v1/customers JSON { "email": "[email protected]", "name": "Pat" } পাঠানো হলে সার্ভার সবসময় একই ত্রুটি আকার রিটার্ন করে এবং সবসময় request_id অন্তর্ভুক্ত করে।
ইমেল অনুপস্থিত বা ভুল ফরম্যাটে। ক্লায়েন্ট ফিল্ড হাইলাইট করতে পারে।
{
"request_id": "req_01HV9N2K6Q7A3W1J9K8B",
"error": {
"code": "VALIDATION_FAILED",
"message": "Some fields need attention.",
"details": {
"fields": {
"email": "must be a valid email address"
}
}
}
}
ইমেল আগেই আছে। ক্লায়েন্টকে সাইন ইন করার বা অন্য ইমেল বেছে নেওয়ার পরামর্শ দেওয়া যায়।
{
"request_id": "req_01HV9N3C2D0F0M3Q7Z9R",
"error": {
"code": "ALREADY_EXISTS",
"message": "A customer with this email already exists."
}
}
কোনো ডিপেন্ডেন্সি ডাউন আছে। ক্লায়েন্ট ব্যাকঅফ নিয়ে পুনরায় চেষ্টা করতে পারে এবং শান্তভাবে একটি মেসেজ দেখাতে পারে।
{
"request_id": "req_01HV9N3X8P2J7T4N6C1D",
"error": {
"code": "TEMPORARILY_UNAVAILABLE",
"message": "We could not save your request right now. Please try again."
}
}
একটি চুক্তি থাকলে ক্লায়েন্টের প্রতিক্রিয়া ধারাবাহিক হয়:
details.fields ব্যবহার করে ফিল্ডগুলো চিহ্নিত করবেrequest_id‑কে সাপোর্ট আইডি হিসেবে দেখাবেসাপোর্টের জন্য একই request_id অভ্যন্তরীণ লগে আসল কারণ পর্যন্ত সরাসরি পথ সরবরাহ করে, স্ট্যাক ট্রেস বা ডাটাবেস ত্রুটি ফাঁস না করেই।
ক্লায়েন্টদের বিরক্ত করার দ্রুততম উপায় হলো তাদের অনুমান করতে বাধ্য করা। যদি এক এন্ডপয়েন্ট { "error": "..." } ফেরায় আর অন্যটি { "message": "..." }, প্রতি ক্লায়েন্ট একজোড়া বিশেষ কেসে পরিণত হয় এবং বাগ সপ্তাহ ধরে লুকিয়ে থাকে।
কয়েকটি ভুল বারবার দেখা যায়:
code না থাকা।request_id যোগ করা, ফলে সফল কলগুলির সাথে সম্পর্ক করা যায় না।অভ্যন্তরীণ তথ্য ফাঁস করা সবচেয়ে সহজ ফাঁদ। একটি হ্যান্ডলার কেবল err.Error() রিটার্ন করে দিলে একটি কনস্ট্রেইন্ট নাম বা থার্ড‑পার্টি মেসেজ প্রোডাকশনে চলে যায়। ক্লায়েন্ট মেসেজ নিরাপদ ও সংক্ষিপ্ত রাখুন, বিস্তারিত কারণ লগে রাখুন।
শুধু টেক্সটের উপর নির্ভর করাও ধীরে ধীরে সমস্যা করে। যদি ক্লায়েন্টকে ইংরেজি বাক্যগুলো পার্স করে “email already exists”‑এর মতো নির্ণয় করতে হয়, আপনি মেসেজ বদলে দিলে লজিক ভেঙে পড়বে। স্থিতিশীল ত্রুটি কোড আপনাকে মেসেজ পরিবর্তন, অনুবাদ, এবং আচরণ বজায় রাখার স্বাধীনতা দেয়।
ত্রুটি কোডকে আপনার পাবলিক চুক্তির অংশ হিসেবে বিবেচনা করুন। যদি বদলাতে হয়, একটি নতুন কোড যোগ করুন এবং পুরনো কোডকে কিছু সময় ধরে কাজ করতেই দিন, এমনকি যদি উভয়ই একই HTTP স্ট্যাটাসে ম্যাপ করে।
শেষে, প্রতিটি রেসপন্সে একই request_id ফিল্ড রাখুন, সাফল্য বা ব্যর্থতা—যখন ব্যবহারকারী বলে “এটা কাজ করছিল, পরে ভেঙে গেল”, সেই একটি আইডি প্রায়শই এক ঘণ্টার গেসিং বাঁচায়।
রিলিজের আগে একটি দ্রুত কনসিস্টেন্সি পরীক্ষা করুন:
error.code, error.message, request_id)।VALIDATION_FAILED, NOT_FOUND, CONFLICT, UNAUTHORIZED)। এমন টেস্ট রাখুন যেন হ্যান্ডলার অচেনা কোড রিটার্ন করতে না পারে।request_id রিটার্ন করুন এবং প্রতিটি রিকোয়েস্টের লগে সেটি রাখুন, এমনকি panic ও টাইমআউট কেসেও।তারপর কয়েকটি এন্ডপয়েন্ট মানুয়ালি স্পট‑চেক করুন। একটি ভ্যালিডেশন ত্রুটি, একটি মিসিং রেকর্ড, এবং একটি অনাকাঙ্ক্ষিত ব্যর্থতা ট্রিগার করুন। যদি প্রতিক্রিয়াগুলো এন্ডপয়েন্ট জুড়ে ভিন্ন দেখায় (ফিল্ড বদলে, স্ট্যাটাস কোড বিচ্যুত হয়, মেসেজ অতিরিক্ত তথ্য ফাঁস করে), শেয়ার করা পাইপলাইন ঠিক করুন নতুন ফিচার যোগ করার আগে।
প্রায়োগিক নিয়ম: যদি কোনো মেসেজ একটি আক্রমণকারীকে সাহায্য করবে বা স্বাভাবিক ব্যবহারকারীকে বিভ্রান্ত করবে, তা লগে রাখুন — রেসপন্সে নয়।
আপনি চাইলে API আগে থেকেই লাইভ থাকুক, তবুও একটি শেয়ার করা ত্রুটি চুক্তি (স্ট্যাটাস, স্থিতিশীল ত্রুটি কোড, নিরাপদ মেসেজ, এবং request_id) লিখে রাখুন। এটি ক্লায়েন্টদের জন্য ত্রুটিগুলো পূর্বানুমানযোগ্য করে তোলার দ্রুততম উপায়।
তারপর ধীরেই মাইগ্রেট করুন। বিদ্যমান হ্যান্ডলারগুলো রাখতে পারেন, কিন্তু তাদের ব্যর্থতাগুলোকে এক ম্যাপারের মাধ্যমে রুট করুন যা অভ্যন্তরীণ ত্রুটিকে আপনার পাবলিক রেসপন্স আকারে রূপান্তর করবে। এটি ঝুঁকিপূর্ণ বড় রিফ্যাক্টরের প্রয়োজন ছাড়াই সামঞ্জস্য বাড়ায় এবং নতুন এন্ডপয়েন্টগুলোকে নতুন ফরম্যাট উদ্ভাবন করা থেকে রোধ করে।
একটি ছোট ত্রুটি কোড ক্যাটালগ রাখুন এবং সেটিকে API‑এর অংশ হিসেবে আচরণ করুন। কেউ নতুন কোড যোগ করতে চাইলে দ্রুত রিভিউ করুন: এটা কি সত্যিই নতুন, নাম স্পষ্ট কি না, এবং ঠিক HTTP স্ট্যাটাসে ম্যাপ হচ্ছে কি না।
কয়েকটি টেস্ট যোগ করুন যা ড্রিফট আটকায়:
request_id আছে।error.code উপস্থিত এবং ক্যাটালগের অংশ।error.message নিরাপদ এবং কখনো অভ্যন্তরীণ বিবরণ নেই।যদি আপনি সম্পূর্ণ নতুন Go ব্যাকএন্ড বানান, শুরুতেই কনট্র্যাক্ট লক করে রাখা সাহায্য করে। উদাহরণস্বরূপ, Koder.ai (koder.ai)‑এ একটি planning mode আছে যেখানে আপনি ত্রুটি স্কিমা ও কোড ক্যাটালগ আগে থেকেই নির্ধারণ করে রাখতে পারেন, তারপর API বাড়ার সাথে সাথে হ্যান্ডলারগুলোকে সামঞ্জস্য রাখতে পারবেন।
একটি JSON ফর্ম্যাট সব এন্ডপয়েন্টে একইভাবে ব্যবহার করুন। একটি বাস্তবসম্মত ডিফল্ট হচ্ছে শীর্ষ স্তরের request_id এবং একটি error অবজেক্ট যাতে থাকে code, message, এবং ঐচ্ছিক details — যাতে ক্লায়েন্ট নির্ভুলভাবে পার্স করে প্রতিক্রিয়া অনুযায়ী কাজ করতে পারে।
error.message‑কে সংক্ষিপ্ত ও ব্যবহারকারী-বান্ধব বাক্য হিসাবে রাখুন এবং প্রকৃত কারণ সার্ভারের লগে রাখুন। কাঁচা ডেটাবেস ত্রুটি, স্ট্যাক ট্রেস, অভ্যন্তরীণ হোস্টনেম বা তৃতীয়‑পক্ষের বার্তা কখনোই রেসপন্সে পাঠাবেন না — উন্নয়নের সময়ও অবশেষে সেটা প্রোডাকশনে চলে যেতে পারে।
HTTP স্ট্যাটাস ব্রড ক্যাটাগরি বোঝায়, কিন্তু যান্ত্রিক লজিকের জন্য স্থির error.code খুব জরুরী। ক্লায়েন্টরা মেশিন‑রিডেবল error.code (যেমন ALREADY_EXISTS) অনুসরণ করে শাখা করতে পারে, স্ট্যাটাস কেবল সাধারণ নির্দেশ দেয় (উদাহরণ: 409 রাজ্য সংক্রান্ত সংঘাত বোঝায়)।
400 ব্যবহার করুন যখন রিকোয়েস্ট সঠিকভাবে পার্স বা ব্যাখ্যা করা যায় না (ম্যালফর্মড JSON, ভুল টাইপ)। 422 ব্যবহার করুন যখন রিকোয়েস্ট সঠিকভাবে পার্স হয়েছে কিন্তু ব্যবসায়িক নিয়ম ভেঙে (যেমন ইমেল ফরম্যাট ভুল, পাসওয়ার্ড খুব ছোট)।
409 ব্যবহার করুন যখন ইনপুটটি সঠিক কিন্তু সার্ভারের বর্তমান অবস্থা সঙ্গে সংঘর্ষ করছে (ইমেল ইতিমধ্যে আছে, ভার্সন মিলছে না)। 422 ব্যবহার করুন ক্ষেত্র-স্তরের ভ্যালিডেশন জন্য যেখানে মান পরিবর্তন করলেই সমস্যা দূর হয়।
একটি ছোট সেট টাইপ করা ত্রুটি তৈরি করুন (validation, not found, conflict, unauthorized, internal) এবং হ্যান্ডলারেরা সেগুলো রিটার্ন করুক। পরে একটি শেয়ার করা ট্রান্সলেটর সেই টাইপগুলোকে HTTP স্ট্যাটাস ও স্ট্যান্ডার্ড JSON আউটপুটে ম্যাপ করবে — ফলে প্রতিক্রিয়া স্থিতিশীল থাকে।
প্রতিটি রেসপন্সে request_id রিটার্ন করুন, সাফল্য হোক বা ব্যর্থতা, এবং প্রতিটি সার্ভার লগ লাইনে সেটি লগ করুন। কোন ব্যবহারকারী সমস্যা জানান, সেটি এক বাড়তি আইডি পাঠালে ওই ID দিয়েই সঠিক লগ লাইন খুঁজে পাওয়া যায়।
কেবলমাত্র যখন অপারেশন সফল তখনই 200 রিটার্ন করুন। ত্রুটির ক্ষেত্রে উপযুক্ত 4xx/5xx ব্যবহার করুন। 200‑এর ভেতর { "ok": false } রাখলে ক্লায়েন্টকে বডি পার্স করতে বাধ্য করে এবং inconsistent আচরণ সৃষ্টি করে।
সাধারণভাবে retry করা ঠিক নয়: 400, 401, 403, 404, 409, 422 — কারণ এগুলোতে ক্লায়েন্ট‑সাইড পরিবর্তন ছাড়া পুনরায় চেষ্টা কাজে আসে না। 503 সাধারণত retry‑যোগ্য, এবং 429 কখনো কখনো (প্রতিশ্রুতি বা ব্যাকঅফের পর) retry করা যায়; idempotency key থাকলে POST‑ও পুনরাবৃত্তি নিরাপদ হতে পারে।
কিছু “গোল্ডেন” টেস্ট রাখুন যা কনট্র্যাক্ট লক করে: প্রতিটি ত্রুটি রেসপন্সে request_id আছে কিনা; স্ট্যাটাস কোড টাইপ‑এর সাথে মিলে কি না; error.code ক্যাটালগের অংশ কিনা। নতুন কোড যোগ করলে পুরনো আচরণ ভেঙে গেলে টেস্ট ফেল করবে, ফলে ক্লায়েন্ট অবাক হবে না।