مصاحبه ها
روند تکامل پایگاه داده ها – مصاحبه با مارتین فاولر و پرامود سدالگه
این مطلب عیناً از وب سایت مطلبچه های مهندسی نرم افزار برداشته شده است که با همت جناب محمدعلی بزرگ زاده به زیبایی ترجمه شده است و مهندسی داده، با هدف جمع آوری مطالب مناسب فارسی در حوزه کلان داده به بازنشر آن پرداخته است .
میهمانان این برنامه پرامود سدالگه و مارتین فاولر هستند. در این اپیزود، در مورد تکامل پایگاه داده (Database Evolution) و توسعه چابک پایگاه داده صحبت میشود. همچنین در مورد چالشهای اصلی در کار با پایگاههای داده در یک فرهنگ توسعه چابک صحبت میشود و اینکه طراحی پایگاه داده و مهمتر از همه، تکامل آن را چطور میتوان در چرخههای اغلب کوتاهی جای داد که در روشهای پیشرفته توسعه نرمافزار وجود دارد. همینطور در مورد یکپارچهسازی مستمر تغییرات کد مرتبط با پایگاه داده با استفاده از اسکریپتهای تغییر شِمای پایگاه داده، استفاده از چندین شِما برای غلبه بر استقرار متمرکز تیمهای توسعه، چگونگی مهاجرت دادن دادهها در گامهای افزایشی و ابزارهایی که میتوانند در این محیطهای چابک استفاده شوند صحبت میشود.
لطفاً خودتان را معرفی کنید.
مارتین: من مارتین فاولر هستم. یک نویسنده و سخنران پُرسر و صدا در مهندسی نرمافزار هستم.
پرامود: من پرامود سدالگه هستم. همانطور که مارتین گفت من خیلی به مهندسی نرمافزار علاقهمندم. من خاصه مبحث دادهها در هر نوع برنامهای را دوست دارم و شِمای پایگاه داده (Schema) مانند NoSQL و چیزهای از این قبیل را علاقهمندم. فکر میکنم امروزه، امر دادهها نقش مهمی در چرخه توسعه نرمافزار دارد.
بسیار خوب. اجازه دهید در مورد بحث امروزمان یعنی تکامل پایگاه داده صحبت کنیم. شما از تکامل پایگاه داده دقیقاً منظورتان چیست؟
پرامود: همچنانکه نرمافزار تکامل مییابد [دادهها هم تکامل مییابند] و البته نرمافزار به این جهت تکامل مییابد که کسب و کار تکامل پیدا میکند. کسب و کار درواقع تلاش میکند که به اجبارهای بازار واکنش دهد یا تلاش میکند که راه جدیدی برای کارش بیابد. این شاید مربوط به فرآیند باشد، شاید مربوط به محصولات جدیدتر باشد و یا شاید مربوط به راههای جدیدتری برای انجام کارها باشد. اما همانطور که روشهای جدیدتری برای انجام کارها مطرح میشود، نرمافزار باید تکامل یابد. این بدان معناست که پایگاه دادهای که نرمافزار از آن استفاده میکند هم باید تکامل یابد. مثلاً میتواند مانند این باشد که قبلاً کتاب میفروختهام و الان میخواهم ریشتراش یا چیز دیگری بفروشم. بهرحال نرمافزار تغییر میکند و وقتی نرمافزار تغییر میکند آنچه در پایگاه داده ذخیره میکند و آنچه از پایگاه داده بازیابی میکند و هرچیز دیگری که از پایگاه داده بخواهید هم تغییر میکند. یعنی ساختار پایگاه داده و طراحی پایگاه داده باید تغییر کند. این میتواند بر چند چیز دلالت داشته باشد. مثلاً قبل از این ۵۰ کاربر را پشتیبانی میکردهام و الان نیاز دارم که ۵ میلیون کاربر را پشتیبانی کنم. این میتواند امور مربوط به پایگاه داده را تغییر دهد یا میتواند علاوه بر آن فرآیندهای کسب و کار متفاوتی را هم منجر شود. بنابراین همه اینها ایده تکامل پایگاه داده را در برمیگیرد. همانطور که گفتم از آنجاییکه نیازمندیها تغییر میکند، بنابراین تکامل پایگاه داده هم رخ میدهد.
نکته خوب این است که ما در مورد تکامل خود سیستمهای پایگاههای داده صحبت نمیکنیم مثلاً اینکه MySQL چطور در طی زمان تکامل یافته است. ما در مورد تکامل نرمافزاری که از یک پایگاه داده استفاده میکند صحبت میکنیم و اینکه پایگاه داده و یا شِمای (Schema) آن تغییر میکند. بیشتر شِمای آن و یا دادههای آن در طی زمان تغییر میکند.
مارتین: بله. و من آن را خیلی مرتبط با تغییر نحوهی تفکرمان به توسعه نرمافزار میبینم. از این جهت که ما این دیدگاه را داشتیم که تنها راه معقولانه ساختن نرمافزار این است که قبل ازکار ساختمان آن، طراحیاش را درآوریم و بعد، آن ساختمان را به طراحی نگاشت دهیم. اما آنچه افرادی مانند من و کلاً جنبش چابک (Agile Movement) از آن حمایت میکند این است که باید فرآیند مستمرتری داشته باشیم و نیاز داریم که بتوانیم تغییرات را هم رسیدگی کنیم. وقتی ما رویه چابک را در روزهای اولیه آن آغاز کردیم که به حدود سال ۲۰۰۰ برمیگردد یکی از مباحثهها این بود که: آیا واقعاً میتوانیم آن را بر روی یک سیستم پایگاه دادهی رابطهای اجرا کنیم؟ در این زمینه، پرامود نقش بارزی داشته است، نه تنها از این نظر که نشان دهد که این کار را میشود کرد بلکه درواقع آن را در یک پروژه بزرگمقیاس، انجام داده است. و این یک گام خیلی قابل توجهی است چون خیلی از پروژهها مبتنی بر رسیدگی به حجم زیادی از دادههای ذخیرهشده در پایگاههای رابطهای هستند. بنابراین مجبورید که بفهمید چطور آن پایگاه داده را تکامل دهید. و این از تکامل کد، پیچیدهتر است چون نه تنها باید تغییرات ساختاری در پایگاه داده بدهید بلکه باید در این مورد هم تصمیم بگیرید که با دادهها چه کنید. چون نمیتوانید مانند کاری که در مورد طراحی کدها میکنید، دادهها را دور بریزید و آنها را دوباره بسازید. درواقع باید به این هم فکر کنید که چطور دادهها را مهاجرت دهید.
فکر میکنید در رسیدگی به دادهها به یک روش چابک و تکاملی، مهمترین مطلب چیست؟
پرامود: چیزهای زیادی هست مثلاً چیزهای کوچکی از قبیل یکپارچهسازی مستمر (Continuous Integration) وجود دارد که چطور جنبه پایگاه دادهاش را فراهم میکنید. مورد دیگر، انشعاب دادن (Branching) است؛ اینکه در انشعاب چه رخ میدهد و چطور کدها را مهاجرت میدهید، نه فقط کدهای برنامه بلکه کدهای پایگاه داده را چطور مهاجرت میدهید. اینکه کدام نسخه درست است و بهرحال نسخهبندی یک مشکل بزرگ است. جنبه دیگر این است که آیا میتوانید به افراد، یک Sandbox بدهید؟ آیا میتوانید خودکارسازی کنید؟ چون در توسعه نرمافزار افراد نمونههای دیگری از برنامه را بر روی ماشین خودشان بالا میآورند. آیا میتوانید این کار را با پایگاه داده بکنید؟ چطور میتوانید این کار را خیلی ساده بکنید؟ خصوصاً برای توسعهدهندهها، آیا میتوانید تنظیماتی داشته باشید که افراد بتوانند نمونههای خود را سریع اجرا کنند. مخصوصاً در یک سناریوی تست واحد (Unit Test) آیا میتوانید بدون هزینه خیلی زیاد، برای خودتان آغاز کنید و تست واحدتان را انجام داده و همه چیزهای [تولیدشده در جریان تست] را دور بریزید و دوباره از نو آغاز کنید؟ و چطور همه اینها را داخل یک چرخه یکپارچهسازی مستمر قرار میدهید؟
اینها اغلب مربوط به جنبه توسعه آن میشود اما اگر در زنجیره پیشتر بروید [مشکلات دیگری هم مطرح میشود مثلاً میدانیم] نیازمندیها و ویژگیهای جدید به شکل مدام، مستقر میشود. حال چطور محیط QA را در حالت خوبی نگه داریم که تنها ویژگیهای جدید و تغییرات جدید در پایگاه داده به آنها داده شود اما با [اعمال] آنها، تستهای موجود بهم نریزد؟ بهمین ترتیب، چطور میخواهید آن را به محیط عملیاتی ببرید؟ وقتی رشته مدامی از تغییرات دارید، چطور بدون اینکه دسترسپذیری سیستم را تحت تأثیر قرار دهید، آن را مستقر میکنید؟ چطور این کار را میکنید بدون اینکه دادههای واقعی موجود در پایگاه داده را تحت تأثیر قرار دهید از این جهت که با کارهای اشتباه آن را خراب نکنید؟ وقتی رشته مدامی از تغییرات دارید همه این چیزها مطرح میشود.
اینها اغلب مربوط به جنبه توسعه آن میشود اما اگر در زنجیره پیشتر بروید [مشکلات دیگری هم مطرح میشود مثلاً میدانیم] نیازمندیها و ویژگیهای جدید به شکل مدام، مستقر میشود. حال چطور محیط QA را در حالت خوبی نگه داریم که تنها ویژگیهای جدید و تغییرات جدید در پایگاه داده به آنها داده شود اما با [اعمال] آنها، تستهای موجود بهم نریزد؟ بهمین ترتیب، چطور میخواهید آن را به محیط عملیاتی ببرید؟ وقتی رشته مدامی از تغییرات دارید، چطور بدون اینکه دسترسپذیری سیستم را تحت تأثیر قرار دهید، آن را مستقر میکنید؟ چطور این کار را میکنید بدون اینکه دادههای واقعی موجود در پایگاه داده را تحت تأثیر قرار دهید از این جهت که با کارهای اشتباه آن را خراب نکنید؟ وقتی رشته مدامی از تغییرات دارید همه این چیزها مطرح میشود.
بگذار آن را کمی مشهودتر کنیم و به زندگی روزمره توسعهدهندهها بپردازیم. برای یک توسعهدهنده عادی، رسیدگی به پایگاه داده به یک طریق تکاملی و چابک به چه شکلی خواهد بود؟
پرامود: فرض کنیم شما یک توسعهدهنده هستید و یک ویژگی [جدید] دارید که رویش کار میکنید. در جلسه سرپایی صبح (Morning Standup) به شما یک ویژگی منسوب میشود. این ویژگی که: «نیاز دارم یک صفحه بسازم که بتوانم یک مشتری را جستجو کنم و اگر پیدا نشد بتوانم مشتری را ایجاد کنم.» بنابراین وقتی میخواهید این ویژگی را توسعه دهید، سریعترین راهش این است که ببینید آیا مشتری را میتوان پیدا کرد یا آيا راهی هست که مشتری را در پایگاه داده پیدا کرد؟ اگر نباشد از خود میپرسید که: آیا میتوانم برایش یک View بسازم؟ یا میتوانم نوعی Stored Procedure برای جستجوی آن بسازم؟ یا میتوانم یک Select برایش اجرا کنم؟ نیاز دارید که بفهمید چطور میخواهید این کار را بکنید. بعد View یا Stored Procedure یا دستور Select را داخل کد جاوا یا هر چیزی که داشته باشید میسازید. کار بعدی که انجام میدهید مربوط به این است که اگر پیدا نشد باید ذخیرهاش کنید. چطور باید آن را ذخیره کنید؟ آیا جدول مشتری که دارید برای همه ویژگیهایی که میخواهید ذخیره کنید ستونهای لازم را دارد؟ اگر ندارد، باید به جدول اضافهاش کنید یا باید یک جدول جدید بسازید یا کارهایی از این قبیل بکنید. اگر نیاز دارید که جدول جدیدی بسازید، باید ببینید که چه چیزهایی باید ذخیره کنید و درواقع چه ستونهایی باید بسازید. اگر در این مسیر بیافتید، در نهایت به یک اسکریپت برای تولید View و یک اسکریپت برای اضافه کردن ستونها یا ساختن جدول جدید میرسید. بنابراین وقتی این کار را بکنید آنگاه آمادهاید که آن را داخل trunk یا خط اصلی توسعه بفرستید. در این زمان همه کدهای جاوا یا کدهای برنامهنویسی را به همراه همه تغییراتی که در پایگاه داده دادهاید را به سیستم کنترل نسخ (Version Control) خود میفرستید. نکتهای که در اینجا وجود دارد این است که نه تنها نرمافزار تغییر میکند بلکه پایگاه داده هم تغییر میکند و همه آنها مربوط به یک درون فرستادن تغییرات هستند. این بعداً وقتی میخواهید آن را بر روی محیط دیگری مستقر کنید، کمک میکند. یعنی اینکه بتوانید تغییرات پایگاه داده را مانند کد، اعمال کنید.
بسیار خوب. گام بعدی این است که حال که کد و تغییرات در سیستم کنترل نسخ قرار گرفته، سیستم یکپارچگیسازی مستمر (Continuous Integration) اجرا میشود و شاید بصورت خودکار برخی تستهای واحد را بر روی آن اجرا میکند و شاید برخی تستهای یکپارچهسازی (Integration Test) با استفاده از نوعی پایگاه داده واقعی باشد که روی آن اجرا میشود. پایگاه داده چطور تغییرات را میگیرد؟
پرامود: ابتدای کار، ما اشاره داشتیم. همانطور که مارتین در مورد سال ۲۰۰۰ گفت شما نیاز به ابزار CI ای دارید که پایگاه داده واقعی را تنظیم کند. من میدانم که خیلیها توصیه میکنند پایگاه داده را خصوصاً برای تستهای واحد و اینجور چیزها دست نزنیم. اما اگر پایگاه داده را به عنوان یک مؤلفه اصلی برنامهتان داشته باشید نیاز دارید که CI را روی پایگاه داده واقعی اجرا کنید. همانطور که در مثالم گفتم اگر فرض کنیم که اسکریپتی برای ساختن یک جدول و یک اسکریپت برای ساختن یک View دارید، این اسکریپتها باید توسط یک نمونه از CI بر روی پایگاه داده واقعی اجرا شوند و بگذاریم که جدول ایجاد شود، بگذاریم View تولید شود و باید تستهای واحدی داشته باشید که بیشتر شبیه تستهای یکپارچهسازی هستند و به پایگاه داده وصل شده، چیزی در جدول قرار میدهند، چیزی را پس میگیرند و از View جستجو میکنند و از این قبیل چیزها. و وقتی اینها رخ داده باشد و وقتی Build از چنین نمونه CI ای آمده باشد آنگاه میدانید که این نسخه خاص پایگاه داده درواقع با نسخه برنامه جلو میرود.
بزرگترین مشکلی که من در این نوع تنظیمات برنامههای پایگاه داده دیدهام این است که اگر از چنین نمونه CI ای استفاده نکنید به این مشکل میخورید که برنامهتان را بر روی پایگاه داده مستقر میکنید اما نسخه پایگاه داده درست نیست یا نسخه برنامه درست نیست و وقتی برنامه اجرا میشود به خطاهای ستون پیدا نشد و جدول پیدا نشد و اینجور چیزها میخورید. اگر اسکریپتهای پایگاه داده را همانند کد برنامه در سیستم کنترل نسخ داشته باشید و اینها مورد تأیید یک نمونه CI باشند همه این مشکلات میتواند از بین برود.
بزرگترین مشکلی که من در این نوع تنظیمات برنامههای پایگاه داده دیدهام این است که اگر از چنین نمونه CI ای استفاده نکنید به این مشکل میخورید که برنامهتان را بر روی پایگاه داده مستقر میکنید اما نسخه پایگاه داده درست نیست یا نسخه برنامه درست نیست و وقتی برنامه اجرا میشود به خطاهای ستون پیدا نشد و جدول پیدا نشد و اینجور چیزها میخورید. اگر اسکریپتهای پایگاه داده را همانند کد برنامه در سیستم کنترل نسخ داشته باشید و اینها مورد تأیید یک نمونه CI باشند همه این مشکلات میتواند از بین برود.
مارتین: مسأله مهمی که اینجا باید ذکر کرد این است که روش معمولی که افراد برای تغییر دادن طرحهای (Schema) پایگاه داده بکار میبرند استفاده از نوعی ابزارهای گرافیکی زیبا است. اینها ابزارهای خوبی هستند و به خوبی و سادگی میتوان از آنها استفاده کرد اما مشکلشان این است که خود را به سیستم کنترل نسخ گره نمیزنند. آنها با ایده محیط نسخهدار تحت کنترل، خوب جور نمیشوند. در نتیجه آنچه در پروژههای ما رخ میدهد این است که اطمینان مییابیم که همه تغییرات پایگاه داده از طریق اسکریپتهای مهاجرت دادن کوچکی انجام میشود که شامل دستورات SQL مربوط به تعریف دادهها (DDL) هستند که میگویند پایگاه را از این حالت به حالت بعدی تغییر بده. همه تغییرات مربوط به شِمای پایگاه داده، به شکلی از اینگونه اسکریپتها، ثبت و ضبط میشود. این روشی است که ما در سیستم کنترل نسخ، تغییرات کد را به تغییرات پایگاه داده گره میزنیم.
و به این معنا هم هست که همه توسعهدهندهها و هم ماشین CI ای که برای اجرای تستها واحد استفاده میشود همگی باید نمونههای مجزا و متفاوت خود از پایگاه داده را داشته باشند؟
پرامود: شما میتوانید به روشهای مختلفی اجرایش کنید. یک راه این است که هر ماشینی نمونه پایگاه داده خود را داشته باشد فرضاً MySQL بر روی همه ماشینها اجرا شود. اگر از پایگاه دادههای تجاریتر مانند Microsoft SQL یا Oracle استفاده میکنید میتوانید به یک ماشین مشترک اما پایگاه دادههای متفاوتی بر روی آن ماشین وصل شوید. مثلاً در پایگاه داده Oracle، پرامود میتواند به شِمای (Schema) پرامود وصل شود در حالیکه مارتین به شِمای مارتین وصل شود. از لحاظ فنی آنها شِماهای مستقلی هستند بنابراین میتوانند کارهای مستقلی انجام دهند اما همچنان بر روی یک ماشین باشند.
اما اگر در یک پروژه ۲۰ نفره یا ۳۰ نفره باشم ممکن است خیلی کار بر سر پایگاه داده بریزد.
پرامود: نه واقعاً. در پروژه بزرگی که من و مارتین در سال ۲۰۰۰ داشتیم، حدود ۴۰ توسعهدهنده داشتیم و فکر میکنم برای تحلیل و QA هم ۲۰ تا داشتیم که در کل به اندازه ۶۰ تا میرسد و ما توانستیم به راحتی با ۲ پایگاه داده کار را اجرا کنیم. یکی از آنها به افراد QA و تست و آنالیز اختصاص یافته بود و دیگری برای همه توسعهدهندهها و نمونههای CI بود. اینطور فکر کنید که پایگاه داده، یک پایگاه داده عملیاتی با انداره کامل نیست. اینجاست که ما از اصطلاح Sandbox استفاده میکنیم؛ برای اینکه توسعهدهنده، تست واحدش را اجرا کند و تستهای مقدماتی را انجام دهد نیازی به پایگاه دادهای با اندازه کامل ندارد.
هرگاه من از وجود چندین نمونه صحبت میکنم: افراد به ناگاه این بحث را پیش میکشند که یعنی مثلاً ۱۳ نمونه از پایگاه دادههای عملیاتی را باید اجرا کنم؟ نه، اینطور نیست. آنچه اجرا میکنید ساختار پایگاه داده عملیاتی است یعنی همه جداول و View ها و Stored Procedure ها آنجا هستند اما دادههای به اندازه نسخه عملیاتی را ندارید. شما واقعاً برای اجرای تستهای توسعهدهنده به یک ترابایت داده نیاز ندارید. شما شاید به ۴ تا مشتری و ۱۰ تا سفارش و ۱۵ قلم جنس و … نیاز داشته باشید. و همه اینها میتواند اسکریپت شود و با استفاده از یک target در ant یا هر روش دیگری در پایگاه داده قرار گیرد اما پایگاه داده شما واقعاً خیلی کوچک است یعنی مقدار دادههایتان خیلی کم است با این حال ساختار آنها یکسان است.
هرگاه من از وجود چندین نمونه صحبت میکنم: افراد به ناگاه این بحث را پیش میکشند که یعنی مثلاً ۱۳ نمونه از پایگاه دادههای عملیاتی را باید اجرا کنم؟ نه، اینطور نیست. آنچه اجرا میکنید ساختار پایگاه داده عملیاتی است یعنی همه جداول و View ها و Stored Procedure ها آنجا هستند اما دادههای به اندازه نسخه عملیاتی را ندارید. شما واقعاً برای اجرای تستهای توسعهدهنده به یک ترابایت داده نیاز ندارید. شما شاید به ۴ تا مشتری و ۱۰ تا سفارش و ۱۵ قلم جنس و … نیاز داشته باشید. و همه اینها میتواند اسکریپت شود و با استفاده از یک target در ant یا هر روش دیگری در پایگاه داده قرار گیرد اما پایگاه داده شما واقعاً خیلی کوچک است یعنی مقدار دادههایتان خیلی کم است با این حال ساختار آنها یکسان است.
متوجه شدم. این اسکریپتهای کوچک که شِمای پایگاه داده را به شکل مستمر تغییر میدهند آیا شامل پارهای کدهای SQL برای مهاجرت دادن دادهها هم هستند که برای این گام مهاجرت، لازم باشد؟
پرامود: بله، ناچار است که اینطور باشد. مثلاً فرض کنید که ابتدا من مشتری و آدرس مشتری، همهاش را در یک جدول داشتهام و وقتی جلوتر میروم نیازمندیها تغییر میکند و نیاز میشود که بتوانم آدرسهای ترابری متفاوتی با آدرسهای صورتحسابها داشته باشم. بنابراین نیاز دارم که جدول آدرس را از جدول مشتری مجزا کنم. بنابراین اسکریپتی مثلاً برای تولید جدول آدرس مینویسم و بعد نیاز دارم که دادهها را از جدول مشتری به جدول آدرسها، جابجا کنم و بعد ستونهای مربوط به آدرس را از جدول مشتری حذف کنم. همه اینها یک پیرایش (Refactoring) منفرد یا یک نیازمندی منفرد است که ما پیادهسازی میکنیم. وقتی این پیادهسازی را کردیم نیاز داریم که اطمینان یابیم که دادهها را هم جابجا کردهایم. مارتین در ابتدای بحث گفت که در پیرایش کد، وقتی کد را کامپایل میکند همه چیز انجام شده است چون کد حامل وضعیت نیست اما در پیرایش پایگاه داده، اینطور نیست و شامل وضعیت دادهها است و نیاز دارید که نه تنها در مورد ساختار بلکه در مورد دادهها بیشتر دقت کنید چون اگر دادهها را از دست بدهید، پیرایشی که انجام دادهاید ارزشی ندارد. هیچکس از دست دادن دادهها در پایگاه داده را دوست ندارد. بنابراین نیاز دارید که اطمینان یابید وقتی که تغییرات ساختاری را انجام دادهاید، دادهها را هم جابجا کردهاید و هم کدتان با ساختار جدید کار میکند. هر ۳ تای اینها باید بصورت موازی و یا همگام انجام شود.
مارتین: خیلی از افراد فکر میکنند که مهاجرت دادهها کاری بزرگ و ترسناک است چون باید از یک شِما به شِمای دیگری بروند و کلی داده برای مهاجرت دادن دارند. این خیلی اعصابخردکن و پیچیده میشود و من پروژههایی را میشناسم که برای مهاجرت دادهها به دردسر واقعی افتادهاند چون اغلب در نهایت پیچیدهتر از چیزی میشود که افراد فکر میکنند. اما آنچه در این تکنیک رخ میدهد این است که آن را به مهاجرتهای مجزای خیلی کوچک میشکنید. مثلاً فقط یک ستون را از یک جدول به جدول دیگری میبرید. یک کار خیلی کوچک با یک تغییر خیلی کوچک در شِما رخ میدهد و مهاجرت دادههای خیلی کمی با آن همراه خواهد شد. چیز واقعاً زیبای آن این است که این تغییرات کوچک خیلی عالی با هم ترکیب میشوند. بنابراین با زنجیر کردن تغییرات کوچک در پیِ هم میتوانید به تغییرات بزرگ برسید. این درواقع شبیه به هر فرآیند انتقال دیگری است. همواره یک فرآیند انتقال بزرگ میتواند به تعدادی فرآيند انتقال کوچک و خیلی ساده شکسته شود و این چیزی است که اینجا رخ میدهد.
شما خیلی وقتها اشاره کردید که این خیلی شبیه به پیرایشهای (Refactor) معمول کد است. همانطور که یک کد وجود دارد که آن را تغییر میدهید، یک شِمای پایگاه داده هم هست که آن را شاید در گامهای ریزی تغییر میدهید. من از بحث پیرایش کد و کتابهایی که در آن باره خواندم به خاطر میآورم که فهرستی از انواع شرایط معمول و نحوه برخورد با آنها و نحوه شکستن کار پیرایش به گامهای کوچک وجود داشت. آیا برای کار با پایگاههای داده هم چیز مشابهی در ذهن دارید؟ آیا شرایط معمولی پیش میآید؟
پرامود: بله، قطعاً. کتاب Database Refactoring همهاش در ارتباط با همین فهرست چیزهایی است که میتوانید در پایگاه داده انجام دهید. اسکات امبلر و من آن را فکر میکنم در ۶ سال پیش نوشتیم و فهرستی از پیرایشهایی دارد که میتوانید به کار ببرید. و این فهرست را میتوان به تغییرات ساختاری، تغییرات مربوط به کیفیت دادهها و چیزهای دیگر دستهبندی نمود. در آن بیش از ۶۰ نوع پیرایش فراهم شده است. هرکدام از آن پیرایشها را میتوانید برداشته و به چندین روش اعمال کنید. در کتاب، ما در مورد فاز میانی که فاز انتقالی خوانده میشود صحبت کردهایم. اگر برنامه شما با پایگاه دادهتان صحبت میکند و کس دیگری با آن پایگاه داده صحبت نمیکند میتوانید بروید و تغییرتان را اعمال کنید مثلاً بدون اینکه مشکل زیادی داشته باشید، میتوانید جدول مشتری را به جدول طرف قرارداد تغییر نام دهید. کد را تغییر میدهید، آنگاه پایگاه داده را تغییر میدهید و همه چیز خوب است و خوشحال هستید. اما در یک سیستم معظم به این سادگی نیست. از این جهت که جدول مشتری ممکن است توسط ۱۰ برنامه دیگر استفاده شود. ممکن است چندین دسته کار برای صدور دادهها (Export) باشد که دادهها را سرکشی میکنند یا انواع دیگری از کارهای مربوط به انبار دادهها و اینجور چیزها باشد. بنابراین ساده نیست. آنچه داریم یک فاز میانی است که ابتدای آن مشتری است و در انتها باید طرف قرارداد باشد اما در این میانه افرادی که به جدول مشتری ارجاع میدهند باید بتوانند جدول مشتری را بگیرند و کدهای جدیدی که از جدول طرف قرارداد صحبت میکنند باید بتوانند جدول طرف قرارداد را بگیرند. بنابراین یک فاز انتقالی در این میان است که باید قبلی را با جدید کنار هم نگه داریم. این فاز انتقالی خصوصاً در سیستمهای معظم یا وقتی تعداد زیادی برنامه با هم صحبت میکنند و در پایگاهداده مینویسند، خیلی مهم است. در کتاب پیرایش پایگاه داده (Database Refactoring) درباره تکنیکهای متفاوت زیادی صحبت شده است که میتوانید انجام دهید. در این سناریوی خاص که جدول مشتری به طرف قرارداد تغییر نام داده است میتوانید یک View داشته باشید که نامش مشتری باشد و نام جدول اصلی مشتری را به طرف قرارداد تغییر دهید. به این ترتیب، افرادی که تلاش میکنند جدول مشتری را بگیرند همچنان میتوانند از طریق View آن را بگیرند و افرادی که تلاش میکنند جدول طرف را بگیرند میتوانند از طریق نام واقعی خود جدول، آن را بگیرند. بنابراین هم نسخه قدیمی و هم نسخه جدید همزمان کار میکنند. اگر چنین تکنیکهایی را بکار ببرید قدیمی و جدید در کنار هم به خوبی کار میکنند.
وقتی شما با چنین تغییراتی به محیط عملیاتی میروید، هنگامیکه در مورد اعمال کردن این اسکریپتها فکر میکنم، مهاجرت دادن (Migrate) دادهها ممکن است زمان زیادی ببرد چون ممکن است حجم زیادی داده وجود داشته باشد و انتقال دادهها از یک جدول به جدول دیگر یا حتی اجرا کردن منطقهایی در اینباره -چون نیاز دارید که منطقهایی را اجرا کنید تا بفهمید که چه چیزی را به کجا منتقل کنید- میتواند زمان زیادی ببرد. شاید خیلی زیادتر از این باشد که بشود منتشرش کرد. آیا این نوع فاز انتقالی را برای چنین شرایطی بکار میبرید؟
پرامود: فاز انتقالی بر این اساس مشخص نمیشود که مهاجرت دادن چقدر طول میکشد بلکه بر این اساس مشخص میشود که کسب و کار چه میخواهد. ما شرایطی را دیدهایم که نسخه ۲ برنامه بهمراه نسخه ۳ برنامه و همینطور نسخه ۴ برنامه همگی در حال کار هستند. بنابراین نمیتوانید تغییراتی را در نسخه ۴ بدهید که بخواهد نسخه ۲ را خراب کند و در این شرایط، باید یک فاز انتقالی داشته باشید که در آن هم نسخه ۲ و هم نسخه ۴ کار کند. بنابراین براساس طول زمان، مهاجرت مشخص نمیشود بلکه براساس دیگر نیازمندیها است مثلاً اینکه آیا لازم است دیگر سیستمها درحال کار باقی بمانند.
با این تفسیر، به سراغ سئوال شما در مورد مدت زمان خود اسکریپت مهاجرت برویم. ما تکنیکهایی را بکار گرفتهایم که بوسیله آنها مهاجرت را در یک محیط پیشعملیاتی اعمال کردهایم و فهمیدهایم که چقدر زمان میبرد، اینکه آیا در یک مدت معقول اجرا میشود و آیا در پنجره زمانی که در اختیار داریم به پایان میرسد یا خیر. آنگاه اگر متوجه شویم که بیش از آنچه انتطار داشتیم یا بیش از زمانی که داریم، طول میکشد میتوانیم برویم و خود عملیات مهاجرت را بهینهتر کنیم تا در پنجرهای که در اختیار داریم به پایان برسد.
با این تفسیر، به سراغ سئوال شما در مورد مدت زمان خود اسکریپت مهاجرت برویم. ما تکنیکهایی را بکار گرفتهایم که بوسیله آنها مهاجرت را در یک محیط پیشعملیاتی اعمال کردهایم و فهمیدهایم که چقدر زمان میبرد، اینکه آیا در یک مدت معقول اجرا میشود و آیا در پنجره زمانی که در اختیار داریم به پایان میرسد یا خیر. آنگاه اگر متوجه شویم که بیش از آنچه انتطار داشتیم یا بیش از زمانی که داریم، طول میکشد میتوانیم برویم و خود عملیات مهاجرت را بهینهتر کنیم تا در پنجرهای که در اختیار داریم به پایان برسد.
و اگر ممکن نباشد؟
پرامود: اگر ممکن نباشد، مهاجرت را [به چند گام] میشکنیم و در چند فاز آن را اعمال میکنیم و یا اینکه آنها را از پیش اعمال میکنیم. در بعضی جاها که چنین کاری کردهایم برخی مهاجرتها را از پیش اعمال کردهایم و بعداً تنها مابهالتفاوت [باقیمانده از دادههای جابجا نشده] را جابجا کردهایم. مثلاً ۵ میلیون مشتری را از قبل جابجا کردهایم و برای هر تغییر جدیدی که رخ دهد بعداً تنها همان مابهالتفاوت را در پنجره زمانی که در اختیار داریم اعمال میکنیم.
بسیار خوب. آیا ممکن است کمی بیشتر در مورد این فاز انتقالی صحبت کنید؟ شاید برخی الگوها (Pattern) یا سرمشقهای (Practice) متداول و معروف در آن باشد.
پرامود: حتماً. یکی از الگوهایی که همین حالا ذکر کردهایم «الگوی بستهبندی تغییر نام با یک View» است. در همان جدول مشتری که به جدول طرف قرارداد تغییر نام داده شد و یک View به نام مشتری داشتیم که همان Select * from party است [از این الگو استفاده کردیم]. به این ترتیب افرادی که با جدول مشتری صحبت میکردند میتوانند همچنان صحبت کنند و افرادی که میخواهند با جدول طرف قرارداد صحبت کنند، آن جدول هم فراهم است. این یکی از تکنیکهایی است که در فاز انتقال انجام میشود.
مورد دیگر استفاده از Triggerها برای وقتی است که ستونها را تغییر نام میدهید و یا وقتی که ستونها را میشکنید. اگر ستونی را تغییر نام داده باشید یا به جای دیگری فرستاده باشید، بروزرسانیها باید در هر دو طرف منعکس شود چون نمیخواهید دادهها بیات یا تاریخ گذشته شوند. بنابراین Triggerهایی را بکار میبرید و دادهها را منتقل میکنید. اگر ستون جدید NOT NULL باشد ستون جدید توسط برنامه بروز میشود و نیاز دارید که دادهها را به ستون قدیمی یا مکان قدیمی بفرستید. و اگر مکان قدیمی بروز شود، تریگر داده را از آنجا بر میدارد و به جای جدید اعمال میکند. بنابراین اساساً هر دو طرف را با هم همگام میکنید.
تکنیک دیگری که استفاده کردهایم استفاده از مقادیر تابعی (Functional Value) یا ستونهای مجازی (Virtual Column) در اوراکل است تا در حالیکه همچنان از آن [نامهای قدیمی] استفاده میکنید نوعی داده میانی را بدهد. ستونهای مجازی، همان ستونهای تابعی هستند که میتوانید در جدول قرار دهید و به شما اجازه میدهد که مقداری را بر اساس برخی ستونها یا توابع موجود، فراهم آورید.
بنابراین میتوانید از تکنیکهای مختلفی استفاده کنید تا اطمینان یابید که همه برنامهها -البته نه لزوماً همه آنها بلکه برنامههای موجودی که نمیتوانند برای روش جدید، تغییر یابند- بتوانند همچنان ادامه یابند و کار کنند و همزمان بتوانید طراحی پایگاه داده را با این تکنیکهای پیرایشی بهبود دهید. و دیگر برنامهها میتوانند به آرامی خود را برسانند چرا که زمان دارند پیرایشهای خودشان را داشته باشند. بنابراین زمانی میرسد که میدانید که حال دیگر نیازی به این فاز انتقالی ندارید چون همه برنامههای دیگر از جدول طرف قرارداد استفاده میکنند. در این زمان، میتوانید بروید و View مشتری که بر روی جدول طرف قرارداد ساخته بودید را پاک کنید.
مورد دیگر استفاده از Triggerها برای وقتی است که ستونها را تغییر نام میدهید و یا وقتی که ستونها را میشکنید. اگر ستونی را تغییر نام داده باشید یا به جای دیگری فرستاده باشید، بروزرسانیها باید در هر دو طرف منعکس شود چون نمیخواهید دادهها بیات یا تاریخ گذشته شوند. بنابراین Triggerهایی را بکار میبرید و دادهها را منتقل میکنید. اگر ستون جدید NOT NULL باشد ستون جدید توسط برنامه بروز میشود و نیاز دارید که دادهها را به ستون قدیمی یا مکان قدیمی بفرستید. و اگر مکان قدیمی بروز شود، تریگر داده را از آنجا بر میدارد و به جای جدید اعمال میکند. بنابراین اساساً هر دو طرف را با هم همگام میکنید.
تکنیک دیگری که استفاده کردهایم استفاده از مقادیر تابعی (Functional Value) یا ستونهای مجازی (Virtual Column) در اوراکل است تا در حالیکه همچنان از آن [نامهای قدیمی] استفاده میکنید نوعی داده میانی را بدهد. ستونهای مجازی، همان ستونهای تابعی هستند که میتوانید در جدول قرار دهید و به شما اجازه میدهد که مقداری را بر اساس برخی ستونها یا توابع موجود، فراهم آورید.
بنابراین میتوانید از تکنیکهای مختلفی استفاده کنید تا اطمینان یابید که همه برنامهها -البته نه لزوماً همه آنها بلکه برنامههای موجودی که نمیتوانند برای روش جدید، تغییر یابند- بتوانند همچنان ادامه یابند و کار کنند و همزمان بتوانید طراحی پایگاه داده را با این تکنیکهای پیرایشی بهبود دهید. و دیگر برنامهها میتوانند به آرامی خود را برسانند چرا که زمان دارند پیرایشهای خودشان را داشته باشند. بنابراین زمانی میرسد که میدانید که حال دیگر نیازی به این فاز انتقالی ندارید چون همه برنامههای دیگر از جدول طرف قرارداد استفاده میکنند. در این زمان، میتوانید بروید و View مشتری که بر روی جدول طرف قرارداد ساخته بودید را پاک کنید.
به نظرم میرسد که نیاز است دقت داشته باشید که وقتی که انتقال انجام شده باشد روشهای دورزدنی (Workaround) که ساختهاید را پاک کنید. بیشتر همانند پیرایش کد به نظر میرسد که مثلاً برای اینکه کاری را راحتتر کنید یک تابع اضافی میسازید و باید بخاطر داشته باشید که بعدش، آن تابع قدیمی را پاک کنید تا کد را تمیز کنید.
پرامود: دقیقاً مانند تابعهای نهیشده (Deprectated) هستند و به این معنا است که: به این مورد دیگر نیازی نیست، آن را بیرون بیاورید چون ما آن را برای همیشه نگه نمیداریم.
مارتین: بله و قطعاً این یک قاعده کلی است که اگر چیزهایی دارید که قدیمی هستند و دیگر استفادهشان نمیکنید باید آنها را پاک کنید چون در غیر اینصورت اگر فایده روشنی از آن نمیبرید، دارید پیچیدگی اضافی میکنید.
و به نظر، مانند شرایط مرسومی است که اغلب هنگام گامهای تکاملی، رخ میدهد. اگر همواره پیرایشهای ریز و تغییرات کوچک و ریز در کد دارید و همواره پیرایشهای پایگاه داده را دارید، باید واقعاً درباره پاک کردن چیزهای قدیمی مواظب باشید. درسته؟
پرامود: بله. وقتی مجموعهای از چیزها داشته باشید که با آنها جلو میروید مثلاً CI و کدنویسی برآمده از تست (Test Driven) و تستهای یکپارچهسازی (Integration Test) و چیزهای دیگر اینچنینی داشته باشید، در اینصورت بیرون کشیدن چیزهایی که دیگر استفاده نمیکنید هم خیلی سادهتر میشود. اگر به هر مکان توسعه نرمافزاری بروید که از این تکنیکها استفاده نمیکنند خواهید دید که آنها خیلی میترسند که هر جور تغییری در پایگاه داده بدهند. به این خاطر که میگویند: ما نمیدانیم چه کس دیگری از این استفاده میکند، ما نمیدانیم از این چطور استفاده میشود. اما اگر از چنین تکنیکهایی استفاده کنید درواقع در این مورد مطمئنتر هستید که پایگاه داده چگونه دارد استفاده میشود. بنابراین در این شرایط تغییر دادن پایگاه داده، نسبت به آن حالت ندانمها، سالمتر و سادهتر است.
اگر برخی از این اسکریپتهای مهاجرت دادن، در محیط عملیاتی، شکست بخورد، چه رخ میدهد؟ یا اینکه اگر یک روز بعد از اینکه به محیط عملیات رفت متوجه [خرابیها و مشکلات] بشوید؟ یا اگر لازم باشد که برخی چیزها را به عقب برگردانید؟
پرامود: تکنیکهای زیادی وجود دارد. اغلب، این فریمورکها هستند که چنین امکانی را به شما میدهند. شاید زمان آن رسیده است که مفهوم مهاجرت دادن و چگونگی پشتیبانی از آن را معرفی کنیم. مهاجرت یک تغییر است که در پایگاه داده میدهید که پایگاه داده را یک گام جلو میبرد. بنابراین، آنها همواره تغییرات افزایشی هستند. مثلاً در یک مهاجرت میتوانید یک ستون را پاک کنید، خیلی اهمیت ندارد. اینها تنها تغییرات ترتیبی رو به جلو هستند. بیشتر فریمورکها به شما اجازه میدهند که از اینجور کارها بکنید. مثلاً Rail یک فریمورک برای مهاجرت دادن دارد. dbdeploy فریمورک دیگری برای مهاجرت دادن است که ThoughtWorks منتشر کرده است. LiquiBase یا dbmaintain یا MyBatis Migrations هم هستند. اینها همه فریمورکهایی هستند که اجازه انجام چنین کارهایی را به شما میدهند. همه آنها، این مفهوم مهاجرت به عقب را دارند. مثلاً فرض کنید که جدول جدیدی ایجاد میکنیم که جدول آدرس است. بنابراین مهاجرتمان میشود: «جدول آدرس را بساز» اما مهاجرت به عقب برای این میشود که: «جدول با نام آدرس را پاک کن». بنابراین هر مهاجرتی هم یک مهاجرت رو به جلو دارد و هم مهاجرت رو به عقب. فرضاً اگر ۴ مهاجرت باشد که بخواهید اعمال کنید، بیشتر این فریمورکها از تکنیک ترتیبی استفاده میکنند مثلاً مهاجرتها را از ۱ تا ۴ بصورت ترتیبی شمارهگذاری میکنند بنابراین میدانید که کدامیک از مهاجرتها اعمال شده و کدامیک اجرا نشده است. مثلاً فرض کنید مهاجرتهای شماره ۱ تا ۱۴ را در پایگاه داده اعمال کردهایم و روز بعد میفهمیم که یک چیزی اشتباه است و باید به عقب برگردیم. در اینصورت تمام کاری که باید بکنید این است که به فریمورک مهاجرت بگویید که پایگاه داده را به مهاجرت شماره ۹ یا ۸ یا هر عدد دیگری برگرداند. فریمورک همه مهاجرتهای رو به عقب را برداشته و آن اسکریپتها را در ترتیب معکوس اعمال میکند مثلاً اول ۱۴ بعد ۱۳ و به همین ترتیب تا ۹. بنابراین همه این فریمورکها از چنین مفهومی پشتیبانی میکنند.
چیزی که باید به خاطر داشت این است که مهاجرت رو به عقب، خیلی حساس است. مثلاً فرض کنید برخی پیرایشها را انجام دادهاید و دادهها در جدول آدرس قرار گرفتهاند و در مهاجرت رو به عقب، جدول آدرس را پاک میکنید اما چون برنامهتان برای ۲-۳ ساعت بالا بوده است، آدرسهای موجود در جدول [که در این مدت درج شده] را از دست میدهید. بنابراین باید خیلی مراقب باشید، نه فقط اینکه مراقب باشید بلکه باید تأثیرات چیزهایی که در مهاجرتهای رو به جلو یا رو به عقب میافتد را بشناسید. حتی در مهاجرت رو به جلو، شرایطی مانند این وجود دارد که مثلاً جدولی را پاک میکنید یا از اینجور کارها میکنید. اگر میدانید که بعداً به دادههایش نیاز دارید یا ممکن است نیاز داشته باشید، بهتر است که جدول را تغییر نام دهید و آن را همچنان نگه دارید و بعداً بیایید و آن را پاک کنید. بنابراین این تکنیکها هم در مهاجرتهای رو به عقب و هم در مهاجرتهای رو به جلو حتماً کمک میکنند.
چیزی که باید به خاطر داشت این است که مهاجرت رو به عقب، خیلی حساس است. مثلاً فرض کنید برخی پیرایشها را انجام دادهاید و دادهها در جدول آدرس قرار گرفتهاند و در مهاجرت رو به عقب، جدول آدرس را پاک میکنید اما چون برنامهتان برای ۲-۳ ساعت بالا بوده است، آدرسهای موجود در جدول [که در این مدت درج شده] را از دست میدهید. بنابراین باید خیلی مراقب باشید، نه فقط اینکه مراقب باشید بلکه باید تأثیرات چیزهایی که در مهاجرتهای رو به جلو یا رو به عقب میافتد را بشناسید. حتی در مهاجرت رو به جلو، شرایطی مانند این وجود دارد که مثلاً جدولی را پاک میکنید یا از اینجور کارها میکنید. اگر میدانید که بعداً به دادههایش نیاز دارید یا ممکن است نیاز داشته باشید، بهتر است که جدول را تغییر نام دهید و آن را همچنان نگه دارید و بعداً بیایید و آن را پاک کنید. بنابراین این تکنیکها هم در مهاجرتهای رو به عقب و هم در مهاجرتهای رو به جلو حتماً کمک میکنند.
سئوال بعدی من همین جا است. آيا دستور Drop table معمولاً منع میشود و به جای آن تغییر نام دادن جدول استفاده میشود؟ تا زمانیکه برای مدت طولانی از آن جدول استفاده نشده باشد؟
پرامود: شما باید متوجه شوید که چطور از آن استفاده خواهید کرد. من نمیگویم که نباید هیچگاه از دستور Drop table استفاده کنید یا اینکه همیشه باید این دستور را استفاده کنید؛ بستگی دارد. هر برنامه و نرمافزاری که مینویسید متفاوت است. اگر میدانید که نیاز دارید دادهها کنار بمانند مثلاً جدول مشتری، شامل مشتریهای ۴۰ سال گذشته شما است، نمیتوان با پاک کردن جدول احساس اطمینان داشت. بنابراین بسته به شرایط نیاز دارید ببینید برای شما چه چیزی جواب میدهد و آن را استفاده کنید درواقع گزینه درست برای خود را داشته باشید.
مارتین: وقتی تغییری در پایگاه داده دارید و میخواهید آن را به محیط عملیاتی ببرید، اغلب خوب است که ابتدا تغییردر پایگاه داده را انجام دهید تا فضای لازم برای تغییری که قرار است بعداً در کد رخ دهد را ایجاد کند. بنابراین اگر نیاز دارید که جدول جدیدی را بسازید یا میخواهید ستونی را از جدولی به جدول دیگری ببرید اغلب خوب است که حتی قبل از کدی که از آن استفاده میکند، جدول جدید در پایگاه داده را برپا کنید. دلیلش این است که به این ترتیب اگر مجبور به عقبگرد (Rollback) شوید، شرایط عقبگرد بهتری خواهید داشت چون میتوانید بدون عقبگرد دادن دادهها، کد را به عقب برگردانید و بنابراین از نوشتن دادهها بر روی ساختار جدید پایگاه داده اجتناب میکنید.
پرامود: درواقع، تکنیکهای پیشرفته فراوانی در این زمینه وجود دارد. همانطور که مارتین به شما میگوید تمام پایگاه داده بهمراه تغییرات کد به خط دیگری از توسعه منتقل میشود. درواقع برای اعمال در محیط عملیاتی وقتی پایگاه داده از کد جلوتر باشد، این به شما کمک میکند که این کار را انجام دهید. همینطور برخی افراد هم برای اعمال مهاجرتها از یک پایگاه داده جانشین استفاده میکنند و بعد مثلاً در نیمهشب آن [پایگاه داده جانشین را با پایگاه داده اصلی] تعویض میکنند. درواقع عملیات تعویض کردن، خیلی آنی رخ میدهد. [برای این منظور] پایگاه داده را رونوشتبرداری (Replicate) میکنید یا از تکنیکهای مشابه استفاده میکنید، آنگاه تغییرات را در طرف دادههای رونوشتشده اعمال میکنید و بعد به سادگی تعویض میکنید. به این ترتیب، هیچ زمان انتظاری برای اجرا شدن مهاجرت وجود نخواهد داشت چون مهاجرت از قبل اجرا شده است. بنابراین تکنیکهای متنوعی برای این کار وجود دارد.
به نظر میرسد ما داریم بیشتر در مورد پایگاههای رابطهای صحبت میکنیم. در مورد این جنبش جدید NoSQL چطور؟ آیا شما در مورد تکامل دادن پایگاه داده در آن محیط هم تجربهای داشتهاید؟
پرامود: بله، حتماً. ما از تعداد زیادی پایگاههای سندی (Document Database) خصوصاً MongoDB استفاده کردهایم. گفته میشود که در NoSQL لازم نیست نگران تغییرات شِما باشید و میتوانید یک شیء جدید را ماندگار (Persist) کنید و لازم نیست نگران شیء قدیمی و نحوه تغییر نام دادن ستونها و اینجور چیزها باشید. اما مثلاً فرض کنید که سندی دارید که نام و شهر را به عنوان صفتهایش دارد و دارید آن را ذخیره و بازیابی میکنید؛ حال شما این شیء را پیرایش (Refactor) میکنید به این ترتیب که از این پس، نام، نام کوچک خوانده میشود و صفت جدیدی با عنوان نام خانوادگی هم تعریف میشود و صفت شهر هم همچنان در جایش باقی است. بنابراین الان سند شامل نام کوچک، نام خانوادگی و شهر است و شما شروع به ماندگار کردن اشیاء میکنید. آنچه به شکل پنهان رخ میدهد این است که دیگر نمیتوانید به سند قبلی برسید چون وقتی [سندی را] بازیابی میکنید اساساً سعی میکنید که سندی را برداشته و آن را به شیء خود نگاشت دهید که همان نام کوچک، نام خانوادگی و شهر است و سندهای قدیمی که تنها شامل نام و شهر هستند دیگر نمیتوانند بازیابی شوند و یا نمیتوانند برگردانده (Parse Back) شوند. بنابراین افراد متوجه این گم شدن دادهها نمیشوند. کاری که برخی افراد تمایل به انجامش دارند این است که دو نسخه از شیء داشته باشند و یا دو راه برای برگرداندن آن داشته باشند به این ترتیب که در هرحال سندها را برمیگردانیم، چنانچه نام کوچک و نام خانوادگی را داشته باشد، آن را در این شیء قرار میدهیم و شیء را پس میدهیم و اگر فقط نام و شهر را داشت، همان موقع، نام و نام خانوادگی را از داخل نام تجزیه میکنیم و بعد شهر را اضافه میکنیم و آن را نگاشت میدهیم و پس میدهیم. این یک زحمت برنامهنویسی مضاعف دارد. بنابراین نیاز دارید که متوجه شوید که کجا هزینهاش را میدهید. گاهی افراد میگویند که من میتوانم هر چه بخواهم را نگه دارم و نیاز نیست نگرانش باشم اما [در حقیقت، در آنجا] برای نگه داشتن همه سندها و قابلیت برگرداندن همه آنها، دارید هزینه برنامهنویسی یا پیچیدگی برنامهنویسی را میدهید. این چیزی است که ما آموختهایم و بهمین معنی است که وقتی پایگاهی بدون شِما است مجبورید هزینهاش را در سمت برنامهنویسی و نه در سمت پایگاه داده بپردازید.
آیا فکر میکنید که برخی از این تکنیکهایی که در کتاب نوشتهاید میتوانند در پایگاههای NoSQL هم بکار گرفته شوند؟
پرامود: بله. خصوصاً تکنیکهای مهاجرت را قطعاً در پایگاههای NoSQL هم بکار بردهایم مثلاً وقتی که یک برنامه یا نسخه جدیدی از برنامه را مستقر میکنید باید اطمینان یابید که سندهای پایگاه داده کنونی اجازه میدهند برنامهتان کار کند. یکی از کارهایی که ما انجام دادیم این بود که یک برنامه سنجشگر نوشتیم که یک سند تصادفی را واکشی میکرد و میدید که آیا میتواند آن را [به شیء برنامه] برگرداند. اگر میتوانست آن وقت میفهمید که میتواند پایگاه داده را بخواند. چیزهایی مانند این واقعاً خیلی خوب هستند تا دادهها را از دست ندهید. درواقع خیلی محتمل است که به خاطر اینجور چیزها، دادههای یتیمی بوجود آید که نتوان به آنها دسترسی داشت.
مارتین: بله و اصل اساسی این است که مهاجرت را از طریق تعداد زیادی مهاجرتهای کوچک انجام دهید که هرکدام شِما و دادهها را با همدیگر تغییر دهند. این اصل اساسی، همچنان کار سختی است. فکر میکنم این شباهت مهم در استفاده از SQL یا NoSQL است.
بسیار خوب. ما خیلی در مورد تغییرات پایگاه داده و انجام گامهای ریز و کوچک برای تغییر دادن پایگاه داده صحبت کردیم. به نظر من شاید نیاز به برخی تغییرات سازمانی هم داشته باشیم که بخشهای مختلف سازمان نزدیکتر به هم کار کنند. آیا این درست است؟ در این مورد چه فکر میکنید؟
مارتین: بله، این یک مسأله خیلی بزرگی است. فکر میکنم یکی از چیزهای واقعاً غمانگیزی که در توسعه نرمافزار خصوصاً در فضای شرکتهای معظم (Enterprise) رخ داده است، این است که بین توسعهدهندههای نرمافزار و مدیرسیستمهای پایگاه داده (Database Administrator)، یک حصار واقعی میبینیم و اینها تقریباً حرفههای مجزایی دیده میشود. کتابهای کاملاً متفاوتی، وبسایتهای کاملاً متفاوتی و کنفرانسهای کاملاً متفاوتی دارند و ارتباط خیلی کمی بین این دو هست. خیلی از توسعهدهندهها فکر میکنند که نیاز ندارند که SQL و نحوه کار پایگاه داده را بفهمند و این چیزی است که DBA انجام میدهد و بهرحال این کار DBA است. و خیلی از DBA ها میگویند که ما تنها نیاز داریم که بدانیم پایگاههای رابطهای چطور کار میکنند و تلاش کنیم این توسعهدهندههای احمق با دادهها وَر نروند. فکر میکنم این حصار، مشکل بزرگی ایجاد کرده است. مانند خیلی از حصارهای سازمانی دیگر، کاری که باید بکنیم این است که آنها را بشکنیم و دو گروه را به تعامل خیلی نزدیکتری بگیریم.
پرامود: این تنها مربوط به تعاملات نیست بلکه مربوط به نتایج نهایی هم هست. اگر توسعهدهندههای نرمافزار ندانند که با استفاده از تکنیکهای بهتر پایگاه داده، کارها میتواند بهتر انجام شود آنگاه خود محصول از کمبود دانش رنج خواهد برد. لزوماً نه فقط اینکه توسعهدهندهها، پایگاه داده را نشناسند بلکه افراد طرف پایگاه داده هم [ممکن است] ندانند که نرمافزار چطور از دادهها استفاده میکند و هدف نرمافزار چیست و چه مسأله کسب و کار را میخواهد حل کند. بنابراین اگر هر دو طرف دید بهتری از این موضوع داشته باشند میتوانید به راه حل بهتری برای مسأله کسب و کار برسید. و این که تعامل داشته باشید و واقعاً «یک تیم» بسازید، کلیدی است. ما توصیه میکنیم وقتی کاری در حال انجام است، افراد پایگاه داده، در کنار توسعهدهندههای نرمافزار بنشینند مثلاً اگر پروژهای را راه میاندازید اطمینان یابید که یک فرد پایگاه دادهای وجود دارد که در همواره در آن پروژه قرار دارد و اطمینان یابید که توسعهدهندهها میدانند که به سراغ چه کسی بروند و صحبت کنند و یا چه کسی را فریاد بزنند چون در یک تیم چابک، ترجیح میدهید فرد پایگاه دادهای در همان اتاق یا میز شما نشسته باشد. بنابراین میتوانید با او صحبت کنید و در این گفتگوها از او یاد بگیرید و همچنین کارهایتان را سریعتر به انجام برسانید. من ترجیح میدهم در عوض اینکه بخواهم مثلاً ایمیل یا تلفن بزنم فرد را در مقابلم یا در همان اتاق مربوط به پروژه داشته باشم. به این ترتیب میتوانید با آنها جلسات پای تخته داشته باشید و بقیه تیم هم میتوانند بفهمند چگونه تصمیمات گرفته شده است مثلاً اینکه فلان جدول چطور طراحی شده، تصمیمات معماری چطور گرفته شده است، آیا نیاز است کارهایی را به روش خاصی انجام دهیم و چیزهای از این قبیل. وقتی این کارها در اتاق پروژه انجام شود درواقع کارامدی کل تیم را بهبود میبخشد و برای مسألهای که میخواهید حل کنید نرمافزار بهتری به شما میدهد.
مارتین: در حقیقت یکی از تکنیکهایی که در توضیح این موضوع، به نظرم خیلی جالب است و درواقع از پرامود گرفتهام این است که وقتی که توضیح میداد چطور دوست دارد کار کند یکی از کارهایی که او میخواهد انجام دهد این است که تنها از روی لاگ کارهای توسعهدهندهها، تغییراتی که توسعهدهندهها در شِمای پایگاه داده انجام میدهند را مانیتور کند. چون هر توسعهدهندهای میتواند هر نوع تغییری را در پایگاه داده اعمال کند و وقتی پرامود به عنوان DBA درگیر میشد، تنها تغییراتی که انجام داده بودند را از روی لاگها، تماشا میکرد و اگر چیزی را میدید که کاملاً درست نبود میرفت و با توسعهدهنده صحبت میکرد و راه بهتر انجام آن کار را به او نشان میداد یا میفهمید که توسعهدهنده واقعاً چه کاری را میخواهد انجام دهد. میتوان از طریق مانیتور کردن تغییرات، این تعاملات را برانگیخت.
پرامود: بله. رابطه به این شکل نیست که من میآیم و شما را سرزنش میکنم، بلکه بیشتر به این شکل است که بگذار بفهمم چه میخواهی بکنی و بعد بهبودش دهیم چون با تقابل، مشکل تعاملات حل نمیشود یا کمکی به آن نمیشود. بنابراین من همیشه فکر میکردهام و همچنان فکر میکنم که تعامل باید بکار آید تا در نهایت کارایی بهتر، کیفیت بهتر نرمافزار و چیزهایی بهتری به شما بدهد و نباید به شرایط بدی برای پروژه بیانجامد.
مارتین: در حقیقت، این بحث وسیعتری است که من از دیگر رهبران تیمها و معمارها شنیدهام که: تغییرات را مانیتور میکنید و هرگاه کسی کاری کرد که با آن موافق نیستید به شکل تعاملی از آن استفاده میکنید به این ترتیب که به سراغ آن فرد میروید و متوجه میشوید که دارد چکار میکند و بعد با هم کار میکنید تا راه بهتری برایش بیابید. بنابراین نگرش اعمال قدرت نیست و در عوض این نگرش است که من دانش خوبی فراگرفتم، شاید دارم چیزی را فراموش میکنم، بنابراین در این میان میتوانیم بفهمیم از آن به کجا برویم. من فکر میکنم این یک راه خیلی خوب است که بوسیله آن هر نوع رهبر تیم یا هر فردِ با مهارتهای خاصی میتواند تعامل مؤثری با افراد برقرار کند. و این یکی از مزایای بزرگ استفاده از ابزارهای کنترل نسخ (Version Control) است که به شما اجازه میدهد ببینید که مثلاً همه تغییراتی که ظرف ۲۴ ساعت گذشته داده شده است چه بودهاند. به شما این فرصت را میدهد که به آنچه رخ میدهد چشم بدوزید و چنین چیزهایی را بیابید بدون اینکه نیاز باشد افراد همیشه برای هر تغییری که میخواهند انجام بدهند به شما تکیه کنند.
بله. این خیلی مشابه با روش مرسوم انجام کارها به سبک چابک است. درسته؟
پرامود: بله.
ما به انتهای اپیزود نزدیک میشویم. آیا هیچ توصیه گرانبهایی هست که بخواهید قبل از اینکه بحث را تمام کنیم با مخاطبانمان به اشتراک بگذارید؟
پرامود: قطعاً. همه ابزارهای خودکارسازی که توسعهدهندههای نرمافزار استفاده میکنند میتواند به کار پایگاه داده هم بیاید. شما میتوانید پایگاه داده را تبدیل به بخش خوش پروژه بکنید یا اینکه خودش را یک پروژه بکنید. خیلی از تکنیکهایی که در سمت توسعه نرمافزار استفاده میشود میتواند در پایگاه داده هم بکار گرفته شود. مثلاً همچنانکه برای پیچیدگی کد [چنین ابزارهایی وجود دارد] میتوان کدهایی نوشت که بفهمیم کدام جدولها و کدام ستونها استفاده نشدهاند یا ابزارهای تحلیلی بنویسیم تا بفهمیم بر اساس تعاریف انجام شده در برنامه، چه ایندکسهایی باید [در پایگاه داده] بسازیم. همه این چیزها، در طرف پایگاه داده هم میشود بکار گرفته شود و اینها، افراد سمت پایگاه داده را خیلی کمک میکند چون امروزه دارید از ابزارهای تولید کد یا افزایش کارایی استفاده میکنید تا کار را سادهتر کنید. درواقع در این مورد میتوانید با توسعهدهندهها بیشتر تعامل کنید تا به کیفیت بهتری هم در طراحی پایگاه داده و هم در نرمافزار برسید. بنابراین فکر میکنم استفاده از این تکنیکها به همه کمک میکند.
مارتین: برای من به نوعی شگفتآور است که این تکنیکها بیشتر همهگیر نشدهاند. مکرراً با افرادی مواجه میشوم که اینجور کارها را کاملاً ناممکن میدانند اما ما یک دهه است که آن را انجام میدهیم. اگر به بخش خوشبینانه آن بنگریم، همچنان بهبودهای بسیار زیادی برای سازمانهای توسعه دهنده نرمافزار میتوان داشت که مبتنی بر تکنیکهای کاملاً اثباتشده هستند. به نظرم این طرز فکر خوبی است.
بسیار خوب. از هر دو برای این اپیزود خوب، بسیار ممنونم و امیدوارم به زودی از شما بشنوم.