JSON diff টুল বাছাই: টেক্সট diff, কাঠামোগত diff ও ভিজুয়ালাইজেশন
সংক্ষেপে: টেক্সট diff খুঁজে বের করে ফাইল কোথায় আলাদা; কাঠামোগত diff সিদ্ধান্ত নেয় বিষয়বস্তু সমতুল্য কি না। সঠিক স্তর বাছাই করলে কি-ক্রমকে ঘটনা ভুল করা বন্ধ হয়।

মূল কথা: টেক্সট diff পরিবর্তন খুঁজে বের করে (আগে jq -S দিয়ে কি সাজান), কাঠামোগত diff সমতুল্যতা ঠিক করে (প্রাইমারি কি দিয়ে অ্যারে সারিবদ্ধ করুন), আর ভিজুয়ালাইজেশন কেবল ব্যাখ্যা বা রিভিউ করে; CI-তে তিন স্তর আলাদা রাখুন।
দুই JSON ডকুমেন্ট আলাদা দেখালেই বিষয়বস্তু আলাদা নয়। টুল বাছার আগে সঠিক তুলনা-স্তর বাছুন।
১. তিন ধরনের "diff" এক নয়
অনেক দল "JSON তুলনা" একক কাজ ভাবে, তারপর ভিন্ন পরিস্থিতিতে একই সমস্যায় পড়ে। তিন স্তর আলাদা করা কাজে লাগে:
| স্তর | কী তুলনা করে | কী প্রকাশ করে | সাধারণ ব্যবহার |
|---|---|---|---|
| টেক্সট diff | কাঁচা বাইট ও লাইন | ইনডেন্টেশন, কি-ক্রম, স্পেস, নিউলাইন | কনফিগারেশন ড্রিফট, কোড রিভিউ |
| কাঠামোগত diff | পার্স করা অবজেক্ট ও অ্যারে | কি যোগ/বাদ, মান বদল, অ্যারে উপাদানের পরিবর্তন | API রিগ্রেশন, ডেটা মাইগ্রেশন |
| ভিজ্যুয়াল diff | পার্থক্য কীভাবে দেখানো হয় | পরিবর্তন কোন স্তরে, তার প্রভাব কত বড় | মানুষের রিভিউ, দলীয় আলোচনা |
টেক্সট diff নির্ভরতাহীন, দ্রুত, আর Git-এর সঙ্গে স্বাভাবিকভাবে মেশে। দুর্বলতা হলো এটি JSON সেমান্টিক্স জানে না। সম্পূর্ণ সমতুল্য দুই ডকুমেন্টে কি-ক্রম বা ইনডেন্টেশন আলাদা হলে লাল-সবুজে জ্বলে ওঠে। কাঠামোগত diff উল্টো: আগে পার্স করে পরে তুলনা করে, তাই বোঝে একটি মান কেবল সরে গেছে। কিন্তু সঠিক অ্যারে সারিবদ্ধকরণ কৌশল দরকার; নাহলে একটি ইনসার্শনকে সম্পূর্ণ পুনর্লিখন ভুল পড়া হয়।
ভিজ্যুয়ালাইজেশন তৃতীয় অ্যালগরিদম নয়। এটি অন্য দুই স্তরের আউটপুট রেন্ডার করে যাতে মানুষ দ্রুত নিশ্চিত করতে পারে পার্থক্য প্রত্যাশিত কি না। মানুষের রায়ের জন্য ভালো, স্বয়ংক্রিয় চেকের বিকল্প হিসেবে খারাপ। স্তরগুলো মিশিয়ে দিলে সাধারণত এমন CI পাইপলাইন হয় যা চিরকাল লাল আর দল যা সেটি উপেক্ষা করতে শিখে গেছে।
২. টেক্সট diff: আগে jq -S দিয়ে কি-ক্রম পরিষ্কার করুন
দুই ফাইল দ্রুত দেখতে সবচেয়ে সস্তা সমন্বয় jq -S প্লাস diff -u। -S ফ্ল্যাগ অবজেক্টের কি পুনরাবৃত্তভাবে সাজায়, ফলে যে ডকুমেন্টের বিষয়বস্তু একই কিন্তু কি-ক্রম আলাদা তাদের নরমালাইজ করে:
jq -S . a.json > a.sorted.json
jq -S . b.json > b.sorted.json
diff -u a.sorted.json b.sorted.json
এটি সবচেয়ে সাধারণ ভুয়া-পজিটিভ দূর করে। RFC 8259 অবজেক্টকে ক্রমহীন সংগ্রহ সংজ্ঞায়িত করে, তাই {"a":1,"b":2} ও {"b":2,"a":1} একই মান। টেক্সট diff অবশ্য প্রতিটি লাইন পরিবর্তিত বলে দাগায়। আগে সাজালে লাল ব্লকগুলো আসল সমস্যার অনেক কাছে আসে।
খেয়াল করুন jq -S কেবল কি সাজায়; অ্যারে সাজায় না। JSON-এ অ্যারের ক্রম অর্থবহ, আর [1,2] আর [2,1] একই মান নয়, তাই টুল আপনার হয়ে সেই সিদ্ধান্ত নেবে না। কোনো অ্যারে ধারণাগতভাবে ক্রমহীন সেট হলে তুলনার আগে নিজে প্রাইমারি কি দিয়ে সাজান।
আরেকটি খুঁটিনাটি মনে রাখার মতো: diff পার্থক্য পেলে exit code ১ দেয়, কিন্তু ফাইল অনুপস্থিত বা অনুমতির এররও অ-শূন্য exit দেয়। CI-তে প্রতিটি অ-শূন্য কোডকে "বিষয়বস্তু বদলেছে" ভাববেন না, নাহলে ভুল পাথকে ডেটা পরিবর্তন হিসেবে রিপোর্ট করা হবে। নিরাপদ প্যাটার্ন আগে ফাইল যাচাই করে পরে পার্থক্য হ্যান্ডল করে:
if ! diff -u a.sorted.json b.sorted.json; then
echo "JSON content differs" >&2
exit 1
fi
delta টেক্সট diff-কে পাঠযোগ্য করে
কাঁচা diff -u আউটপুট কাজ করে, কিন্তু লম্বা লাইন ও গভীর নেস্টেড JSON পড়তে ক্লান্তিকর। delta হলো একটি diff ফিল্টার যা সিনট্যাক্স হাইলাইটিং, লাইন নম্বর ও স্পষ্ট ব্যাকগ্রাউন্ড রং যোগ করে:
diff -u a.sorted.json b.sorted.json | delta
এটি শুরু হয়েছিল Git পেজার হিসেবে, তাই git diff-ও নিজের হাতে নিতে পারে। দিনে দশটি কনফিগারেশন diff পড়া মানুষের কাছে এ ধরনের টুল অ্যালগরিদমিক নির্ভুলতার চেয়ে পড়ার গতি বাড়ায়। আরেকটি প্রচলিত পছন্দ git diff --no-index --word-diff, যা শব্দ-স্তরে তুলনা করে এবং খুব লম্বা লাইনে রাখা কম্প্রেসড JSON-এ ভালো কাজ করে।
একটি সতর্কতা: সৌন্দর্যায়ন ভুয়া পার্থক্য দূর করে না। কি-ক্রম নরমালাইজ না করলে delta শুধু কোলাহলকে সুন্দর করে। সঠিক ক্রম সবসময় আগে নরমালাইজ, পরে সাজসজ্জা।
৩. কাঠামোগত diff: jsondiffpatch সেমান্টিক্স বোঝে
প্রশ্ন যখন দুই ডেটা সেমান্টিকভাবে সমতুল্য কি না, কাঠামোগত diff ব্যবহার করুন। JavaScript ইকোসিস্টেমের সবচেয়ে প্রচলিত বিকল্প jsondiffpatch, যা + ও - মার্কারযুক্ত টেক্সট লাইনের বদলে পরিবর্তন বর্ণনাকারী delta অবজেক্ট দেয়:
import { create } from 'jsondiffpatch';
const instance = create();
const delta = instance.diff(
{ id: 1, tags: ['a', 'b'], enabled: true },
{ id: 1, tags: ['a', 'c'], enabled: false },
);
console.log(JSON.stringify(delta, null, 2));
delta যোগ হওয়া কি, বাদ পড়া কি, প্রতিস্থাপিত মান ও সরানো অ্যারে উপাদান আলাদা করে। অর্থাৎ assertion-এ সরাসরি delta ধরে হাঁটা যায়: পার্থক্য না থাকলে undefined, থাকলে পাথ-ধরে বর্ণনা। টেক্সট diff-এর মতো এটি ইনডেন্টেশন, কি-ক্রম বা 1e3 বনাম 1000-এর মতো সাংখ্যিক বানানে বিচলিত হয় না।
সমান গুরুত্বপূর্ণ, কাঠামোগত diff আউটপুট প্যাচিং প্রবাহে দেওয়া যায়। jsondiffpatch patch ও reverse দেয়, তাই delta মূল অবজেক্টে প্রয়োগ করে পরে ফেরানো যায়। একটি সতর্কতা: স্বয়ংক্রিয়ভাবে তৈরি delta আর হাতে লেখা JSON Patch একই ফরম্যাট নয়, তাদের মধ্যে রূপান্তরে পরিশ্রম লাগে। বিস্তৃত তুলনা পদ্ধতির জন্য দেখুন JSON diff ও তুলনা।
৪. কি-ক্রমজনিত ভুয়া পার্থক্য কীভাবে দূর করবেন
কি-ক্রম কেবল বিরক্তিকর নয়; ভুল সিদ্ধান্তও দিতে পারে। তদন্তের সময় তিন ধাপ অনুসরণ করুন:
- তুলনার আগে
jq -Sবা সমতুল্য টুল দিয়ে কি-ক্রম পুনরাবৃত্তভাবে নরমালাইজ করুন। - প্রতি রানে বদলায় এমন ফিল্ড সরান, যেমন
timestamp,requestIdওtraceId। - নিশ্চিত করুন ডুপ্লিকেট কি নেই। ডুপ্লিকেট কি পার্সিংয়ের সময় "শেষটি জয়ী" নিয়মে সমাধান হয়, তাই টেক্সট diff দেখে আর কাঠামোগত diff দেখে না।
jq -S 'del(.meta.timestamp, .meta.requestId)' a.json > /tmp/a.json
jq -S 'del(.meta.timestamp, .meta.requestId)' b.json > /tmp/b.json
diff -u /tmp/a.json /tmp/b.json
এই প্রবাহ কনফিগারেশন ড্রিফটে ভালো মানায়। staging ও production-কে একই নরমালাইজেশনের মধ্য দিয়ে চালান, যা অবশিষ্ট থাকে সেটাই সত্যিই ব্যাখ্যা দরকার এমন পার্থক্য। ফিল্টার নিয়মগুলো সংস্করণযুক্ত স্ক্রিপ্টে রাখুন, স্মৃতি থেকে আবার টাইপ করবেন না। টুলচেইনের সম্পাদনা ও স্ক্রিপ্টিং দিকের জন্য দেখুন এডিটর, কমান্ড লাইন ও অটোমেশন।
৫. অ্যারে: ইনডেক্স নাকি id দিয়ে সারিবদ্ধ?
অ্যারে JSON তুলনার সবচেয়ে এরর-প্রবণ অংশ। ডিফল্ট ইনডেক্স সারিবদ্ধকরণে শুরুতে একটি রেকর্ড ঢোকালে পরের প্রতিটি উপাদান সরে যায়, আর টুল সবগুলোকে পরিবর্তিত দাগায়। টুল ভাঙা নয়; সারিবদ্ধকরণ কৌশল ভুল।
| সারিবদ্ধকরণ | কখন কাজ করে | শক্তি | ঝুঁকি |
|---|---|---|---|
| ইনডেক্স অনুযায়ী | অ্যারের ক্রম ব্যবসায়িক অর্থ বহন করে | সরল, বাড়তি ফিল্ড লাগে না | একটি ইনসার্শন ঝাঁকুনিপূর্ণ ভুয়া-পজিটিভ ঘটায় |
| প্রাইমারি কি অনুযায়ী | উপাদানগুলো অনন্য id-যুক্ত সেট | ইনসার্শন, ডিলেশন ও সরানো ধরে | স্থির, অনন্য কি দরকার |
| দীর্ঘতম সাধারণ অনুক্রম | ক্রম গুরুত্বপূর্ণ কিন্তু ইনসার্শন অনুমোদিত | খাঁটি ইনডেক্সিংয়ের চেয়ে বেশি মজবুত | ব্যাখ্যা কঠিন, খরচ বেশি |
jsondiffpatch objectHash দিয়ে প্রাইমারি কি ঘোষণা করে, তুলনার আগে অ্যারে উপাদান ইনডেক্স করে:
const instance = create({
objectHash: (item: { id?: string; sku?: string }) => item.id ?? item.sku,
});
সাধারণ jq দিয়ে আগে সাজিয়ে পরে টেক্সট diff ব্যবহার করা যায়:
jq -S 'sort_by(.id)' items.json > items.sorted.json
id-সারিবদ্ধকরণের সমঝোতা হলো অবস্থানগত পরিবর্তন আর পার্থক্য হিসেবে গোনা হয় না। ব্যবসা জানতে চাইলে যে একটি রেকর্ড অবস্থান ২ থেকে ৫-এ গেছে, তবে স্পষ্ট ক্রম-ফিল্ডও তুলনা করুন; কেবল সেটের বিষয়বস্তুর উপর নির্ভর করবেন না।
৬. ভিজ্যুয়াল diff: মানুষের জন্য বানানো স্তর
কাঠামোগত diff আউটপুট মেশিনের জন্য, মিটিংয়ের জন্য নয়। অন্য কাউকে পরিবর্তন ব্যাখ্যা করতে হলে ভিজ্যুয়ালাইজেশন বেশি কার্যকর:
- VS Code diff এডিটর ফাইল-ধরে রিভিউয়ের জন্য ভালো, পাশাপাশি প্যান ও ইনলাইন হাইলাইটসহ।
- JSON Crack ও JSON Hero-র মতো ট্রি ভিউয়ার দেখায় পরিবর্তন কোন স্তরে।
jsondiffpatchএকটি HTML ফরম্যাটার দেয় যা delta-কে কলাপসযোগ্য সমৃদ্ধ ভিউ হিসেবে রেন্ডার করে।
ভিজ্যুয়ালাইজেশনের মূল্য প্রসঙ্গ। টেক্সট diff বলে লাইন ৮৮ বদলেছে; ট্রি ভিউ বলে লাইন ৮৮ data.items[3].price-এর অংশ। কিন্তু ভিজ্যুয়ালাইজেশন সাধারণত পুরো ডকুমেন্ট ব্রাউজার বা ডেস্কটপ অ্যাপে লোড করে। প্রোডাকশন পেলোড খোলার আগে নিশ্চিত করুন টুল স্থানীয়ভাবে গণনা করে নাকি সার্ভারে আপলোড করে।
৭. diff-কে CI-তে বসানোর তিনটি অভ্যাস
প্রথম, তুলনার আগে নরমালাইজ করুন। ইনডেন্টেশন পিন করুন, কি পুনরাবৃত্তভাবে সাজান, আর diff চালানোর আগে অস্থির ফিল্ড ফিল্টার করুন। নাহলে CI কোলাহলে বারবার ব্যর্থ হয়।
দ্বিতীয়, সেমান্টিক্সের ভিত্তিতে সারিবদ্ধকরণ বাছুন। অ্যারে সেট হলে প্রাইমারি কি দিয়ে সারিবদ্ধ করুন। ধারা হলে ইনডেক্স সারিবদ্ধকরণ রাখুন। প্রতিটি API-তে একই নিয়ম প্রয়োগ করবেন না।
তৃতীয়, মান যাচাই করুন, টেক্সট নয়। টেস্টে পার্স করা অবজেক্ট তুলনা করা JSON.stringify-এর আউটপুট তুলনার চেয়ে অনেক বেশি স্থির। চেক ব্যর্থ হলে পুরো পেলোড লগে ডাম্প না করে পার্থক্যের পাথ ও আগে/পরে মান ছাপুন।
# পুরো ডকুমেন্ট নয়, কেবল পার্থক্যযুক্ত পাথ ও মান ছাপুন
jq -S 'del(.timestamp)' actual.json > /tmp/actual.json
jq -S 'del(.timestamp)' expected.json > /tmp/expected.json
diff -u /tmp/expected.json /tmp/actual.json | head -n 80
পার্থক্য দীর্ঘমেয়াদে ট্র্যাক করতে হলে নরমালাইজেশন স্ক্রিপ্ট ও প্রত্যাশিত ফাইল একসঙ্গে commit করুন। এককালীন তদন্ত হলে পরে অস্থায়ী স্ক্রিপ্ট মুছে ফেলুন, যাতে নীরবে ব্যবসায়িক লজিকের দ্বিতীয় ইমপ্লিমেন্টেশন না হয়ে ওঠে।
৮. টুল বাছাইয়ের টেবিল
| পরিস্থিতি | প্রথম পছন্দ | বিকল্প | মূল স্মারক |
|---|---|---|---|
| মাঝেমধ্যে ফাইল তুলনা | VS Code diff | diff -u |
কোলাহল দূর করতে আগে ফরম্যাট করুন |
| কনফিগারেশন ড্রিফট | jq -S + diff |
delta |
অস্থির ফিল্ড ফিল্টার করুন |
| API রেসপন্স রিগ্রেশন | jsondiffpatch |
deepdiff |
অ্যারে প্রাইমারি কি দিয়ে সারিবদ্ধ করুন |
| দলকে পরিবর্তন ব্যাখ্যা | JSON Crack / JSON Hero | HTML ফরম্যাটার | ডেটা আপলোড হয় কি না দেখুন |
| খুব বড় ফাইল | jq দিয়ে সাবট্রি বের করা |
jless ব্রাউজিং |
পুরো গাছ মেমোরিতে লোড করবেন না |
| pre-commit চেক | git diff --no-index |
pre-commit | exit code স্পষ্টভাবে হ্যান্ডল করুন |
কোনো একটি সেরা JSON diff টুল নেই; আছে বর্তমান স্তরের জন্য সঠিক টুল। টেক্সট diff পরিবর্তন খুঁজে বের করে, কাঠামোগত diff সমতুল্যতা বিচার করে, আর ভিজ্যুয়ালাইজেশন পরিবর্তন ব্যাখ্যা করে। তিন স্তর আলাদা রাখা সবকিছু করা এক টুল খোঁজার চেয়ে অনেক বেশি নির্ভরযোগ্য। শাখাজুড়ে কনফ্লিক্ট হ্যান্ডলিংয়ের জন্য এগিয়ে যান JSON ও Git merge।
সাধারণ প্রশ্ন
jq -S কি অ্যারের ক্রমও বদলায়?
না। -S কেবল অবজেক্টের কি সাজায় আর অ্যারে নিজের ক্রমে রাখে। JSON-এ অ্যারে ক্রমযুক্ত, তাই টুল [1,2]-কে [2,1] করে না। অ্যারে ধারণাগতভাবে ক্রমহীন সেট হলে sort_by দিয়ে স্পষ্টভাবে সাজান।
কাঠামোগত diff-এ 1 আর 1.0 একই মান?
বেশিরভাগ পার্সার দুটোকেই একই সংখ্যা বানায়, তাই কাঠামোগত diff সাধারণত সমান ধরে, অথচ টেক্সট diff আলাদা দাগায়। সমতুল্যতা ঠিক করতে কাঠামোগত diff আর ফাইল বাইটে বাইটে এক কি না ঠিক করতে টেক্সট diff ব্যবহার করুন।
diff আউটপুট কি সরাসরি JSON Patch হিসেবে ব্যবহার করা যায়?
সরাসরি নয়। diff টুল বলে কী আলাদা, অথচ JSON Patch ক্রমযুক্ত অপারেশন তালিকা বর্ণনা করে; ফরম্যাট ও সেমান্টিক্স আলাদা। স্বয়ংক্রিয় প্যাচও অ্যারে ইনডেক্স নির্দেশ করে, যা কনকারেন্সিতে পুরোনো হয়ে যায়। প্রয়োগের আগে একটি test অপারেশন যোগ করুন।
কয়েক দশ MB-র JSON ফাইল কীভাবে তুলনা করব?
আগে যে সাবট্রি দরকার তা বের করুন, যেমন jq '.data.items' দিয়ে, তারপর প্রাইমারি কি দিয়ে সাজিয়ে তুলনা করুন। পুনরাবৃত্ত diff-এর জন্য পুরো গাছ মেমোরিতে লোড করবেন না। খুব বড় ফাইল স্ট্রিমিং বা চাঙ্কড তুলনায় ভালো হ্যান্ডল হয়, আর মেমোরি খরচ আলাদাভাবে মূল্যায়ন দরকার।
অনলাইন diff টুল কি আমার ডেটা ফাঁস করতে পারে?
ইমপ্লিমেন্টেশনের উপর নির্ভর করে। খাঁটি ফ্রন্টএন্ড টুল ব্রাউজারে গণনা করে, ডেটা আপলোড করে না; সার্ভার-রেন্ডারড টুল বিষয়বস্তু ব্যাকএন্ডে পাঠায়, যেখানে তা লগে ঢুকতে পারে। প্রোডাকশন কনফিগারেশন বা গ্রাহক পেলোড তুলনার আগে নিশ্চিত করুন পেজ স্থানীয়ভাবে গণনা করে, আর দরকার হলে একবার নেটওয়ার্ক বিচ্ছিন্ন করে টেস্ট করুন।
টেক্সট diff আর কাঠামোগত diff-এর মধ্যে কীভাবে সিদ্ধান্ত নেব?
দুই ফাইল কোথায় আলাদা দ্রুত দেখতে jq -S দিয়ে কি সাজিয়ে diff -u চালান। দুই ডকুমেন্ট সেমান্টিকভাবে সমতুল্য কি না ঠিক করতে jsondiffpatch-এর মতো কাঠামোগত diff ব্যবহার করুন এবং অ্যারে প্রাইমারি কি দিয়ে সারিবদ্ধ করুন। টেক্সট diff নির্ভরতাহীন ও Git-এর সঙ্গে মেশে, অথচ কাঠামোগত diff ইনডেন্টেশন, কি-ক্রম বা সাংখ্যিক বানানে বিচলিত হয় না।
CI-তে প্রতিটি অ-শূন্য diff exit code-কে পার্থক্য ভাবা উচিত নয় কেন?
diff পার্থক্য পেলে ১ exit দেয়, কিন্তু ফাইল অনুপস্থিত বা অনুমতির এররও অ-শূন্য ফেরত দেয়। প্রতিটি অ-শূন্য কোডকে "বিষয়বস্তু বদলেছে" ভাবলে ভুল পাথকে ডেটা পরিবর্তন হিসেবে রিপোর্ট করা হয়। নিরাপদ প্যাটার্ন আগে ফাইল যাচাই করে পরে পার্থক্য আলাদাভাবে হ্যান্ডল করে।
কয়েক দশ MB-র ফাইল কি ব্রাউজারে diff করা উচিত?
না। আগে jq দিয়ে দরকারি সাবট্রি বের করুন, তারপর প্রাইমারি কি দিয়ে সাজিয়ে তুলনা করুন। পুনরাবৃত্ত diff-এর জন্য পুরো গাছ মেমোরিতে লোড করা অনেক বেশি মেমোরি ও সময় খায়। খুব বড় ফাইল স্ট্রিমিং বা চাঙ্কড তুলনায় ভালো হ্যান্ডল হয়।