يا جماعة الخير، السلام عليكم ورحمة الله.
اسمحوا لي اليوم أحكي لكم قصة صارت معي في بداياتي، قصة علمتني درس يمكن أهم من أي لغة برمجة تعلمتها. كنت وقتها شاب صغير، مليان حماس وثقة، شايف حالي “أبو العرّيف” في البرمجة. الكود اللي بكتبه بيشتغل زي الساعة، وما في خوارزمية بتصعب عليّ.
وصلتني فرصة في شركة كبيرة كنت بحلم فيها، وبعد المقابلة الأولى، بعثوا لي “المهمة المنزلية” (Take-home assignment). كانت عبارة عن تطبيق صغير، والمطلوب إنجازه خلال 3 أيام. قرأت المطلوب، ضحكت بسخرية وقلت: “هاي بدها 3 أيام؟ هاي شغل ساعتين زمن!”.
وفعلاً، قعدت على مكتبي، و “طق طق طق” على الكيبورد، وفي أقل من 3 ساعات كان كل شيء جاهزاً وشغالاً 100%. رفعت الكود وبعثته وأنا كلي ثقة، وصرت أتخيل حالي قاعد في مكاتبهم الفخمة. بعد يومين، وصلتني رسالة الإيميل اللي كنت بستناها… ولكنها كانت رسالة رفض.
صابتني صدمة. كيف؟ ليش؟ الكود كان يعمل بدون أي خطأ! الإيميل كان لطيفاً ومبهماً: “نقدر وقتك، ولكننا قررنا المضي قدماً مع مرشحين آخرين”. قلت يلا، مش مشكلة، نصيب. ولكن القصة تكررت. شركة ثانية، وثالثة، ورابعة، وخامسة. خمس شركات، خمس مهام منزلية، خمس رسائل رفض بنفس الغموض.
عند الرفض الخامس، كنت محبطاً جداً. شو القصة؟ معقول أنا مش شاطر زي ما أنا مفكر؟ ولكن هذه المرة، كان في جملة صغيرة في إيميل الرفض غيرت كل شيء: “وجدنا صعوبة في متابعة منطق الحل وصيانته مستقبلاً”.
هنا بالزبط، وكأن لمبة نورت فوق راسي. أدركت الحقيقة المرة: أنا كنت أكتب كوداً للكمبيوتر فقط. كود يفهمه المترجم (Compiler)، لكن لا يفهمه الإنسان الذي يراجعه. كانت هذه نقطة التحول في مسيرتي كلها.
لماذا تم رفضي رغم أن الكود يعمل؟
هذا هو السؤال الجوهري. نحن كمبرمجين، خصوصاً في البداية، نعتقد أن الهدف هو “الحل”. إذا كان البرنامج يعمل ويؤدي المطلوب، فقد نجحنا. لكن في عالم العمل الحقيقي، هذا نصف الحقيقة فقط.
المهمة المنزلية ليست مجرد اختبار لقدرتك على حل المشكلة، بل هي نافذة يطل منها فريق التوظيف على عقلك وطريقة عملك. هم لا يريدون فقط معرفة “هل تستطيع؟”، بل يريدون معرفة “كيف ستفعل ذلك كجزء من فريقنا؟”.
ما يبحثون عنه حقًا هو:
- الوضوح (Readability): هل يمكن لزميلك في الفريق أن يفهم الكود الذي كتبته بعد 6 أشهر من الآن بدون أن يحتاج للاتصال بك في منتصف الليل؟
- القابلية للصيانة (Maintainability): هل الكود منظم بطريقة تجعل إضافة ميزة جديدة أو إصلاح خطأ أمراً سهلاً، أم أنه سيتسبب في انهيار كل شيء؟
- الاحترافية (Professionalism): هل تعاملت مع المهمة كمشروع حقيقي (مع توثيق، اختبارات، وهيكلية واضحة) أم كواجب مدرسي؟
الكود الذي يعمل هو الحد الأدنى المتوقع، ليس هو الإنجاز بحد ذاته. الإنجاز الحقيقي هو كتابة كود يعمل، ويكون واضحًا، ومنظمًا، وقابلاً للتطوير.
التشريح: ما الذي يبحث عنه المراجع في الكود الخاص بك؟
بعد ما أكلت الصدمة الخماسية، جلست مع نفسي وبدأت أحلل وأبحث. قرأت كتباً مثل “Clean Code”، وراجعت أكواداً لمشاريع مفتوحة المصدر، وبدأت أفهم اللعبة. إليكم تشريح ما يبحث عنه المراجعون، وكيف يمكنك أن تبهرهم.
أولاً: وضوح الكود (Code Readability) – اكتب قصة، لا شفرة
الكود الجيد يقرأ مثل النثر المنطقي. يجب أن يشرح نفسه بنفسه.
نصيحة أبو عمر: عامل أسماء المتغيرات والدوال كعناوين في قصتك. إذا كانت العناوين غامضة، ستضيع القصة.
- أسماء ذات معنى: هذا هو القانون الأول. ابتعد عن `x`, `y`, `arr`, `data`. استخدم أسماء تصف ما يحتويه المتغير أو ما تفعله الدالة.
// Bad 👎 function proc(d) { let l = []; for(let i = 0; i 18) { l.push(d[i].n); } } return l; } // Good 👍 function getAdultUserNames(users) { const adultUserNames = []; for(const user of users) { if(user.age > 18) { adultUserNames.push(user.name); } } return adultUserNames; } - دوال صغيرة ومحددة: يجب أن تفعل الدالة شيئًا واحدًا فقط (Single Responsibility Principle). إذا وجدت نفسك تكتب دالة تقوم بجلب البيانات، ثم معالجتها، ثم تنسيقها، ثم عرضها… فتوقف! هذه ليست دالة، هذه فوضى. قسمها إلى دوال أصغر.
- التعليقات الذكية: القاعدة الذهبية هي أن الكود يجب أن يشرح “ماذا” يفعل، والتعليقات يجب أن تشرح “لماذا” يفعل ذلك بطريقة معينة. لا تكتب تعليقاً يشرح سطراً واضحاً.
// Bad Comment 👎 // increment i by 1 i++; // Good Comment 👍 // We need to process the last item separately due to an edge case in the API response. processLastItem(items);
ثانياً: هيكلة المشروع (Project Structure) – بيتك المرتب
لا ترمِ كل الملفات في مجلد واحد. تخيل أن المراجع يفتح مشروعك، هل سيجد غرفة مرتبة أم “كراكيب”؟ الهيكلة الجيدة تدل على أنك تفكر في المستقبل وفي زملائك.
حتى لو كان المشروع صغيراً، هيكل بسيط مثل هذا يصنع فرقاً كبيراً:
/project-root
|-- /src
| |-- /components # (For UI components)
| |-- /utils # (For helper functions)
| |-- index.js # (Main entry point)
|-- /tests
| |-- utils.test.js
|-- .gitignore
|-- package.json
|-- README.md
هذا الترتيب يخبر المراجع: “أنا منظم، وأفهم كيف تنمو المشاريع”.
ثالثاً: لا تنسَ التوثيق! (The README.md file)
ملف `README.md` هو بطاقة الترحيب بمشروعك. هو أول ما يراه المراجع. إذا كان فارغاً أو مهملاً، فأنت تعطي انطباعاً سيئاً من البداية. هذا الملف هو فرصتك لشرح عملك والتألق.
ماذا يجب أن يحتوي؟
- وصف للمشروع: ما هو هذا المشروع وماذا يفعل؟
- كيفية التشغيل:
- كيفية تثبيت الاعتماديات (e.g., `npm install`).
- كيفية تشغيل المشروع (e.g., `npm start`).
- كيفية تشغيل الاختبارات (e.g., `npm test`).
- قرارات التصميم والافتراضات: هذا الجزء هو الأهم! هل كان هناك شيء غامض في المتطلبات واتخذت قراراً بشأنه؟ اشرح قرارك هنا. مثلاً: “افترضت أن المدخلات ستكون دائماً باللغة الإنجليزية، لذلك لم أتعامل مع الحروف غير اللاتينية”. هنا بتفرجيهم إنك بتفكر مش بس بتنفذ.
رابعاً: الاختبارات (Testing) – دليلك على الجودة
لا أحد يتوقع منك تغطية اختبارات 100% في مهمة منزلية. لكن كتابة بضعة اختبارات وحدوية (Unit Tests) بسيطة للوظائف الأساسية في الكود الخاص بك تنقلك من “مطور جيد” إلى “مطور محترف”.
هذا يثبت أنك تهتم بالجودة، وتفكر في الحالات الحرجة (Edge Cases)، وتكتب كوداً يمكن الاعتماد عليه. استخدام مكتبة مثل `Jest` في عالم JavaScript سهل جداً ويضيف قيمة هائلة لمشروعك.
مثال عملي: قبل وبعد
لنفترض أن المهمة هي: “كتابة دالة تأخذ نصًا وتعيد عدد تكرار كل كلمة”.
الحل الأول (الذي كان سبب رفضي)
ملف واحد اسمه `index.js`، وهذا محتواه:
// index.js
function count(str) {
var words = str.toLowerCase().split(' ');
var map = {};
words.forEach(function(w) {
if (map.hasOwnProperty(w)) {
map[w]++;
} else {
map[w] = 1;
}
});
return map;
}
var text = "Hello world hello";
console.log(count(text)); // { hello: 2, world: 1 }
هل يعمل؟ نعم. هل هو جيد؟ لا. إنه غامض، لا يتعامل مع علامات الترقيم، لا يمكن اختباره بسهولة، ولا يوجد أي سياق له.
الحل الثاني (حل أبو عمر بعد ما تعلم الدرس)
هنا الوضع يختلف تماماً. لدينا هيكلية مشروع، توثيق، كود نظيف، واختبارات.
1. ملف `README.md`:
# Word Frequency Counter
This is a simple utility to count the frequency of words in a given text.
## How to Run
1. Install dependencies: `npm install`
2. Run tests: `npm test`
3. Run the example: `node src/index.js`
## Design Decisions
- The function converts all text to lowercase to ensure case-insensitivity (e.g., "Hello" and "hello" are treated as the same word).
- It removes common punctuation (., ,, !) before counting to get more accurate results.
2. ملف `src/utils/wordCounter.js`:
/**
* Cleans and normalizes a text string by converting to lowercase and removing punctuation.
* @param {string} text - The input text.
* @returns {string} The cleaned text.
*/
function normalizeText(text) {
if (typeof text !== 'string') return '';
return text.toLowerCase().replace(/[.,!]/g, '');
}
/**
* Counts the frequency of each word in a given text string.
* @param {string} text - The text to analyze.
* @returns {Object} An object mapping each word to its frequency.
*/
export function countWordFrequency(text) {
const cleanedText = normalizeText(text);
if (cleanedText.length === 0) return {};
const words = cleanedText.split(/s+/).filter(word => word.length > 0);
const frequencyMap = words.reduce((map, word) => {
map[word] = (map[word] || 0) + 1;
return map;
}, {});
return frequencyMap;
}
3. ملف `tests/wordCounter.test.js` (باستخدام Jest):
import { countWordFrequency } from '../src/utils/wordCounter';
describe('countWordFrequency', () => {
it('should count words in a simple sentence', () => {
const text = 'hello world hello';
expect(countWordFrequency(text)).toEqual({ hello: 2, world: 1 });
});
it('should be case-insensitive', () => {
const text = 'Hello world hello';
expect(countWordFrequency(text)).toEqual({ hello: 2, world: 1 });
});
it('should handle punctuation', () => {
const text = 'Hello, world! Hello.';
expect(countWordFrequency(text)).toEqual({ hello: 2, world: 1 });
});
it('should return an empty object for an empty string', () => {
const text = '';
expect(countWordFrequency(text)).toEqual({});
});
});
شاهد الفرق! الحل الثاني لا يحل المشكلة فقط، بل يروي قصة. يظهر أن المطور يفكر في الجودة، والحالات الحرجة، والوضوح، والتعاون المستقبلي. هذا هو الفرق بين الرفض والقبول.
الخلاصة: الكود ليس لك وحدك
يا أصدقائي، الدرس الذي تعلمته من تلك الرفوضات الخمسة هو أن البرمجة عمل إنساني قبل أن تكون عملية تقنية. الكود الذي نكتبه هو وسيلة تواصل مع زملائنا في الفريق، ومع أنفسنا في المستقبل.
المهمة المنزلية هي أول محادثة مهنية لك مع الشركة. فاحرص على أن تكون محادثة واضحة، ومنظمة، ومحترفة.
لا تحبطك رسائل الرفض، بل اجعلها وقوداً لتتعلم وتتطور. كل رفض كان بالنسبة لي درساً جعلني مبرمجاً أفضل. تذكر دائماً، اكتب الكود كما لو أن الشخص الذي سيقوم بصيانته من بعدك هو شخص عنيف يعرف أين تسكن. 😉
بالتوفيق يا جماعة! 💪