fa
Feedback
ToCode

ToCode

رفتن به کانال در Telegram

טיפים קצרים למתכנתים מאת ינון פרק

نمایش بیشتر
1 420
مشترکین
اطلاعاتی وجود ندارد24 ساعت
+27 روز
-230 روز
آرشیو پست ها
ToCode
1 420
# טיפ לינוקס: כניסה לסרביסים באמצעות SSH Tunnel צריכים להתחבר לסרביס שרץ על מכונה אחרת אבל נחסמים בגלל חומת אש? הסרביס המרוחק מקשיב רק לחיבורים מ localhost ואתם מגיעים מבחוץ ובכל זאת רוצים לגשת? אל תמהרו לשנות את הגדרות חומת האש או לשנות את ה listen interface. אם יש לכם גישת ssh למכונה הטריק הפשוט הבא יכול לתת פיתרון הרבה יותר מהיר. המתג -L לפקודת ssh פותח תעלה (tunnel) בין המכונה שלכם למכונה המרוחקת, כך שתוכנה תוכל לרוץ על המכונה שלכם ולנסות להתחבר לפורט מסוים - ואוטומטית ההודעה תישלח בתעלה למכונה השניה ותתנהג כאילו התוכנה שלכם רצה על המכונה השניה. בצורה כזאת אפשר להתחבר לבסיס נתונים Production דרך ממשק גרפי, אפילו אם הפורט של בסיס הנתונים סגור ובסיס הנתונים מאזין רק לחיבורים מקומיים. בואו נראה דוגמה עם redis - אני מקים שתי מכונות Ubuntu, מתקין redis על שתיהן ומייצר חיבור ssh ביניהן כך שאפשר יהיה ממכונה אחת להתחבר ב ssh למכונה השניה. כתובות ה ip שלי הן 192.168.64.15 ו 192.168.64.13. קודם כל אני נכנס למכונה 13 ושם מכבה את שרת ה redis כך שחיבור מקומי ייכשל (כדי לא להתבלבל):
$ systemctl stop redis-server
$ redis-cli
Could not connect to Redis at 127.0.0.1:6379: Connection refused
אני גם מריץ את הפקודה הבאה כדי לוודא שיש לי גישת ssh ממכונה 13 למכונה 15:
ssh ubuntu@192.168.64.15 /bin/true
עכשיו נכנס למכונה 15 ושם מוודא שיש לי חיבור ל redis המקומי. אגדיר שם גם מפתח x לדוגמה עם הערך 10:
$ ssh ubuntu@192.168.64.15
$ redis-cli
redis-cli
127.0.0.1:6379> set x 10
OK
ועכשיו מגיעים לקסם. במכונה 13 אני מפעיל:
$ ssh -L 6379:localhost:6379 ubuntu@192.168.64.15
המספר 6379 הוא הפורט של רדיס. אני כותב אותו פעמיים כדי שחיבורים במכונה המקומית ל 6379 יחוברו אוטומטית לפורט 6379 במכונה השניה. הפקודה גורמת להפעלת SSH Session אז אל תופתעו כשאתם מוצאים את עצמכם בתוך shell במכונה 15. נפתח את מכונה 13 בחלון נוסף ושם אני כותב:
$ redis-cli
והפעם הפקודה לא נכשלה. יותר מזה אני יכול להדפיס את הערך של מפתח x ולקבל 10, רק בשביל להיות בטוח שאני מחובר לאותו רדיס:
127.0.0.1:6379> get x
"10"
כלומר הפקודה redis-cli שרצה על מכונה 13 מתנהגת כאילו על המכונה המקומית בפורט 6379 יש שרת redis, ובצד השני החיבור נראה כאילו הוא מגיע מהמכונה המקומית ולכן למרות ש redis מקשיב רק לממשק הרשת המקומי עדיין הצלחנו להתחבר אליו.

ToCode
1 420
# שידרוג וובפאק בתיקיית הדוגמאות בקורס ריאקט אחד הדברים שאני אוהב בקורס ריאקט פה באתר הוא היצירה של פרויקט מאפס בכל הדוגמאות, כאשר לכל דוגמה יש קובץ webpack.config.js משלה. אני יודע שבעולם האמיתי הרבה פעמים תשתמשו ב vite או create-react-app כדי ליצור את תבנית הפרויקט, ואני אפילו מלמד את הכלים האלה במסגרת הקורס, אבל גם מאמין שכשהדוגמאות פשוטות ואפשר להיכנס לכל קבצי ההגדרות ולהבין מה כל שורה שם זה עוזר להבין איך דברים עובדים. (ולא, אתם כנראה לא תצליחו להיכנס לכל קבצי ההגדרות של create-react-app כשאתם רק מתחילים את הדרך בריאקט. אבל עם קובץ וובפאק של 20 שורות אני מאמין שתסתדרו). מה שלא מאוד חשוב לצורך לימוד ריאקט זה הגירסה של וובפאק. פרויקט הדוגמה השתמש ב webpack 4 כי זה מה שהיה באותו זמן שכתבתי אותו. אחד היתרונות בכתיבת קבצי הגדרות פרויקט מאפס זה שהם מספיק פשוטים כדי לשרוד את פגעי הזמן. או לפחות את חלק מפגעי הזמן. לאחרונה גיליתי בדרך הקשה ש Node הוסיפו משהו שהתנגש עם webpack 4, זה שבר כמה דברים ב create-react-app וכן גם את הגדרות הפרויקט הקטנות שלי. יש פרטים בקישור הזה. בכל מקרה כשדברים נשברים זאת תמיד הזדמנות טובה לשדרג. עבור כל הפרויקטים בתיקיית הדוגמאות השינוי היחיד שהיה משמעותי בקובץ ההגדרות היה שינוי האוביקט:
devServer: {
  overlay: true,
},
ל:
devServer: {
  client: {
    overlay: true,
  }
},
בפרויקטים יותר גדולים וקבצי הגדרות יותר מסובכים כנראה שתצטרכו לעבוד יותר קשה. מדריך מפורט לשידרוג קבצי הגדרות וובפאק מ-4 ל-5 אפשר למצוא בקישור הזה. זה עדיין משאיר אותנו עם קורס Webpack שלם פה באתר שמלמד וובפאק 4. עוד נצטרך לעבור ולשדרג גם את כל הדוגמאות שם. בכל מקרה אם אתם בקורס ריאקט פשוט תורידו מחדש את תיקיית הדוגמאות ותקבלו גירסה חדשה נוצצת ועובדת עם ה webpack החדש.

ToCode
1 420
# מצאו את המתחזים! בהינתן העלות הגבוהה של גיוס והזמן הארוך שהרבה פעמים לוקח כדי לזהות שמועמד מסוים שהתקבל בעצם לא כזה מוכשר כמו שחשבנו שיהיה, אפשר להבין למה בהרבה מקומות המטרה של ראיונות עבודה הפכה להיות "לזהות את המתחזים" או "להגן על המוצר". ואני מודה, גם אני פעם נפלתי בבורות האלה. מצד אחד המנהלים רוצים להגדיל את הצוות, ומצד שני מגיע אליך בן אדם להתראיין ואתה מפחד - מפחד שאולי הוא לא יבין את ההוראות כמו שצריך כשתעבדו יחד, מפחד שאולי הוא יכתוב קוד שאחרי זה יסבך אותך או יגרום לך להיראות רע, וכן גם מפחד מהשינוי. נכון, המצב עכשיו אולי לא משהו והפיתוח לא מתקדם מספיק מהר, אבל לפחות זה לא משהו מהסוג שאנחנו מכירים. וככה במקום לחפש איפה הבן אדם שהגיע כן יכול לתרום אתה מוצא את עצמך נכנס למגננה, שואל פרטי טריוויה שאף אחד לא מכיר רק כדי להראות שהמועמד שמולך לא טוב מספיק. יש לי חבר שבראיון בימים הראשונים של האינטרנט לא התקבל לעבודה בגלל שלא ידע איך לצייר פינות עגולות ל div (וכן זה היה לפני שהיה לנו border-radius), וחבר אחר שחזר מבואס מראיון ריאקט אחרי שלא ידע בעל פה את השמות של כל ה Lifecycle Methods שלהם. כשאנחנו מחפשים מתחזים בכל פינה לא מפתיע שאנחנו מוצאים בקלות. הרבה יותר מעניין להבין מי האנשים שמולך ואיזה ערך הם כן יכולים להביא לחברה. הנה כמה שאלות פתוחות ויותר מעניינות שאפשר לשאול בראיון: 1. ספרי על הפרויקט האחרון שבנית. 2. ספרי על אתגרים מעניינים שנתקלת בהם בפרויקט. 3. איזה פיצ'ר היה לך קשה לממש ולמה. 4. איזה פיצ'ר היית בטוחה שהולך טוב, אבל נשבר בפרודקשן? מה קרה שם? 5. ספרי על תקלת פרודקשן מעניינת שטיפלת בה. 6. איך נראה תהליך פיתוח תוכנה אידאלי בינייך? בראיון עבודה כדאי לנסות ליצור שיח ולהבין את היכולות של הבן אדם שמולנו. המטרה היא לבחור מועמד מבין 10 מועמדים טובים והדרך היחידה שזה יקרה היא אם נצליח לראות את הטוב ב-9 שלא נבחר.

ToCode
1 420
# למה כל כך לאט? כשפרויקט מתקדם לאט יותר מהצפוי זה כמעט תמיד בגלל אחת או יותר מהסיבות הבאות: 1. לא מכירים מספיק את הטכנולוגיה. 2. הפרויקט לא מאופיין מספיק טוב וצריך לחקור מה בונים תוך כדי תנועה. 3. הפרויקט דורש שינויים באיך שמידע שמור (בעיקר במבנה בסיס הנתונים) בצורה שדורשת מיגרציה למידע ישן. 4. הפרויקט דורש שינויים בקוד קיים שלא בנוי לשינויים. את שלושת הראשונים קל לראות. הרביעי מצליח להפתיע אותנו כל פעם. אלה המאפיינים המרכזיים של קוד שאינו בנוי לשינויים: 1. אחרי שינוי אני לא יודע מה שברתי. 2. שינוי במקום אחד דורש שינוי רוחב בעשרות מקומות נוספים. 3. הקוד כולל מספר מנגנונים שונים כדי לעשות את אותו דבר (לדוגמה פרויקט ווב שגם כולל Inline Style בקומפוננטות ריאקט וגם CSS). 4. הקוד כולל באגים נסתרים שצצים בעקבות השינוי ומכריחים אותי לצלול למקומות שלא רציתי להגיע אליהם. כשאנחנו נתקלים בקוד שלא מתאים לשינויים הדבר הכי גרוע לעשות זה "לספוג את זה", להישאר שעות נוספות ולבנות מעקפים כדי שהפיצ'ר שלנו יגיע בזמן לפרודקשן. הרבה יותר טוב להתמודד עם הבעיות בקוד ולארגן אותו מחדש כדי שגם הפיצ'ר שאני בונה עכשיו וגם פיצ'רים עתידיים יוכלו להיכנס הרבה יותר מהר. (וכן ברור לי שלפעמים אין ברירה ובונים מעקפים. השאלה רק לאיזה פרק זמן. אם בשנתיים האחרונות השקעתם על כל פיצ'ר חודש יותר ממה שהיה צריך רק בגלל מבנה הקוד, אז כנראה שהיה עולה לכם פחות לשבת פעם אחת לסדר את הקוד ואחרי זה לבנות את שאר הפיצ'רים. כיבוי שריפות זאת לא שיטת עבודה חכמה על פרויקט תוכנה).

ToCode
1 420
# תשתית בתור קוד והחשיבות של הוראות שיחקתי קצת עם AWS השבוע ובדומה למערכות אחרות כשמגיעים לדבר על קוד תשתית מדריכים ברשת מחולקים לשני סוגים: המהיר והנכון. מדריך מהיר יגיד לכם ליצור משאבים דרך ה AWS Console, עם משפטים כמו: > In the AWS Console, create a new WebSocket API מדריך נכון יכלול קוד אוטומטי שיוצר את המשאבים:
policy = iam_resource.create_policy(
    PolicyName=f'{lambda_role_name}-{self.permission_policy_suffix}',
    PolicyDocument=json.dumps({
        'Version': '2012-10-17',
        'Statement': [{
            'Effect': 'Allow',
            'Action': ['execute-api:ManageConnections'],
            'Resource': self.api_arn}]}))
policy.attach_role(RoleName=lambda_role_name)
אין ספק שיותר קל לבנות מערכות עם מדריכים מהסוג השני. האתגר (והחוכמה) הם להבין שדף הוראות לבניית תשתית שווה הרבה פחות מקוד, ולכן אחרי שמסיימים לבנות את התשתית עם מדריך מהסוג הראשון, עדיין יש צורך לקחת את כל תהליך הבניה ולהכניס אותו לקוד. רק בגלל שמי שכתב את המדריך התעצל לא אומר שאנחנו צריכים.

ToCode
1 420
# איך לקרוא ולכתוב קבצי XML ב Python עם ElementTree המודול ElementTree הוא חלק מהספריה הסטנדרטית של פייתון עוד מאז גירסה 2.5. הוא נחשב קל יותר ויעיל יותר בהשוואה ל xml.dom.minidom. בואו נראה איך להשתמש בו בעזרת שלוש דוגמאות פשוטות. ## יצירת מסמך XML מתוך קוד פייתון תוכנית ראשונה לוקחת קוד פייתון ויוצרת מסמך XML שמתאים לו. נתחיל עם הקוד:
import xml.etree.ElementTree as ET

root = ET.Element('items')
for item in ['one', 'two', 'three']:
    child = ET.SubElement(root, item)
    child.set('length', str(len(item)))

ET.dump(root)
אחרי יבוא המודול הפונקציה ET.Element יוצרת אלמנט ב XML, והפונקציה ET.SubElement יוצרת אלמנט ילד בתוך אלמנט אחר. הפונקציה set של אלמנט מגדירה ערך ל Attribute. סך הכל הקוד מדפיס את מסמך ה XML הבא:
<items><one length="3" /><two length="3" /><three length="5" /></items>
כדי לכתוב את המסמך לקובץ אפשר להשתמש בפונקציה write של אלמנט:
root.write('input.xml')
## קריאת מסמך XML מלא אחרי שכתבנו את הקובץ אפשר להתקדם לתוכנית שניה שתקרא אותו ותדפיס את שלושת האלמנטים ממנו. התוכנית משתמשת באותו ElementTree רק שהפעם הלולאה תהיה על האלמנט. הנה הקוד:
import xml.etree.ElementTree as ET

doc = ET.parse('input.xml')
root = doc.getroot()
for child in root:
    print(f'"{child.text}" has length {child.attrib["length"]}')
ב ElementTree האלמנטים מתנהגים כמו אוביקטים רגילים של פייתון - אפשר לרוץ בלולאה על אלמנט כדי לקבל את כל הילדים שלו, אפשר לפנות לאלמנט באינדקס מסוים כדי לקבל ילד ספציפי, ואפשר לגשת למאפיין attrib כדי לקבל את כל ה Attributes או ל text כדי לקבל את הטקסט של האלמנט. ## חיפוש במסמך XML בעזרת XPath פיצ'ר מדליק נוסף של ElementTree הוא התמיכה ב XPath, שמאפשרת חיפוש של אלמנטים ספציפיים בעץ. בדוגמה הבאה אני רוצה למצוא את כל האלמנטים שמאפיין length שלהם הוא 3, כלומר את האלמנטים one ו two. הנה הקוד:
import xml.etree.ElementTree as ET

doc = ET.parse('input.xml')
for child in doc.findall("./*[@length='3']"):
    print(child.text)
הפונקציה findall מקבלת שאילתת xpath ומחזירה רשימה של תוצאות. אם אתם לא בטוחים איך קוראים את ה XPath אז בקישור הזה יש דף קיצורים נחמד שמסביר על כל התחביר. למידע נוסף ועוד דוגמאות על ElementTree שווה גם לבדוק את תיעוד המודול בתיעוד של פייתון בקישור: https://docs.python.org/3/library/xml.etree.elementtree.html

ToCode
1 420
# חדש באתר: שיעורי הרחבה (במקום וובינרים) הי חברים, כמו שחלקכם ודאי שמתם לב השנה לא קבעתי עדיין תאריכים לוובינרים. האמת היא שבשבועות האחרונים התלבטתי לגבי הפורמט של התכנים שאני רוצה להוסיף לאתר. הוובינרים, שהיו מאוד פופולריים בתקופת הקורונה, התחילו להרגיש מיושנים. מצד אחד באותה שעה של וובינר מאוד אהבתי שכולם באים יחד, שאפשר לשאול שאלות ושאפשר להרחיב את הנושא לפי כיוונים שבאמת מעניינים אתכם. אבל מצד שני מי שניסה ללמוד מההקלטות של אותם וובינרים הרבה פעמים הלך לאיבוד או היה צריך לדלג לקטעים הרלוונטים. בעיה נוספת שהיתה לי עם הוובינרים היתה אילוצי השעות - בגלל שהזמנתי את כולם לאותה שעה רציתי ליצור מספיק תוכן שיעניין את כולם, וזה לא השאיר מקום לדברים יותר קטנים אבל עדיין שימושיים. אם לדוגמה רציתי להראות בעשר דקות איזה תוסף לעבודה טובה יותר עם git ב VS Code, הייתי צריך להלביש על זה תוכן לשעה שלמה וזה לא תמיד התאים. הפיתרון שאני הולך לנסות בחודשים הקרובים יהיה הקלטה אסינכרונית של תכני הרחבה, שיהיו קצרים יותר ומדויקים יותר מוובינרים, ויעברו עריכה בדומה לשיעורים בקורסים. המטרה של אותם חיזוקים היא לתת לאנשים שנמצאים לקראת סוף קורס או שכבר סיימו קורס אפשרות להרחיב את הידע על אותו נושא שכבר מכירים. אני מתכנן להתחיל עם חיזוק אחד בשבוע ומקווה להעלות ל 2-3 ככל שאתקדם בבניית התשתיות מסביב. בשלב ראשון שיעורים אלה יהיו סגורים למנויים בלבד כמו הקורסים. אני מקווה ככל שהכמות תגדל להקליט בעתיד גם שיעורים פתוחים, וכשזה יקרה הם יופיעו רגיל גם פה וגם ביוטיוב. בינתיים מוזמנים לבדוק את שיעור ההרחבה הראשון שכבר עלה בנושא git ואיך לבטל מיזוג של Pull Request. השיעור הוא בעצם הרחבה של פוסט שכתבתי על אותו נושא, והוידאו כולל את כל הצעדים בפוסט עם הסבר מפורט על כל אחד והרחבה על הסכנה בביצוע revert ל merge ישן. אם אתם מנויים ועובדים ב git אני בטוח שהוא יעזור לכם. אם יש לכם נושאים קטנים נוספים שהייתם רוצים ללמוד עליהם בהרחבה, מכל אחד מהעולמות שנלמדים פה באתר, אשמח לשמוע ולנסות להוסיף. פשוט תשאירו הודעה או שלחו מייל.

ToCode
1 420
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

ולהוסיף 3 שורות לקובץ ה src/index.css בפרויקט:
@tailwind base;
@tailwind components;
@tailwind utilities;
אתם מוזמנים לנסות את זה ולהדביק את קוד הדוגמה שלי בפרויקט שיצא כדי לראות את טיילווינד בפעולה בפרויקט ריאקט שלכם.

ToCode
1 420
# גם בעבודה עם ריאקט - Tailwind CSS היא רעיון מעולה ספריית Tailwind CSS מציעה פריימוורק מאוד פשוט לכתיבת CSS בלי לכתוב CSS בכלל. הרעיון הוא שבמקום לכתוב מאפיין CSS אנחנו מוסיפים קלאס מיוחד לאלמנט, ולמעשה לכל מאפיין ולכל ערך יש קלאס שמתאים לו. הכח של טיילווינד הוא שקבצי ה CSS מיוצרים דינמית לפי הקלאסים שרשמתם כך שהכל מסתדר וה CSS לא כולל דברים מיותרים. והכח של טיילווינד ביישומי ריאקט הוא שזה פשוט פיתרון הרבה יותר קל והרבה יותר טוב מכתיבת Inline Styles. בואו נראה איך זה עובד ואיך לשלב את טיילווינד ביישום שלכם. ## טיילווינד או Inline Style ברמה הבסיסית עוזר לחשוב על Tailwind בהשוואה ל Inline Style של קומפוננטה. נניח שאני רוצה לבנות קומפוננטה שמציגה קצת טקסט ושני כפתורים, אז אני יכול להשתמש בקוד ריאקט הבא:
function App() {
  return (
    <div className="App">
      <h1>My Cool Game</h1>
      <ul style={{ listStyle: 'none', padding: '0', display: 'flex', flexDirection: 'column' }}>
        <li style={{ margin: '0.5rem 0' }}><MenuButton>Single Player</MenuButton></li>
        <li style={{ margin: '0.5rem 0' }}><MenuButton>Multi Player</MenuButton></li>
      </ul>
    </div>
  )
}

function MenuButton({ href="#", children }) {
  const style = {
    display: 'inline-block',
    width: '10rem',
    textDecoration: 'none',
    background: '#4CAF50',
    border: 'none',
    color: 'white',
    padding: '1rem 1.5rem',
    fontSize: '1.5rem',
  };

  return (
    <a href={href} style={style}>{children}</a>
  );
}

export default App
שמצד אחד הוא מאוד נוח לכתיבה ונראה קל לתחזוקה, אבל בפרויקט גדול ל Inline Style יש מספר חסרונות: 1. קשה להשתמש בקומפוננטה במקום אחר עם הגדרות עיצוב אחרות, כי אי אפשר לדרוס Inline Style מתוך CSS בצורה נוחה. 2. כשמתחילים להכניס Media Queries, Dark Mode או אפילו hover אנחנו מגלים ש Inline Style לבדו לא מספיק וצריך להוסיף ספריה נוספת כמו Styled Components. זה לא בהכרח רע אבל מוסיף סיבוכיות משלו. 3. יש צורך לזכור ערכים שרירותיים ולהשתמש באותם ערכים בין קומפוננטות (לדוגמה גודל השוליים). אותו קוד בהמרה ל Tailwind הולך להיראות מאוד דומה, רק שהגדרות העיצוב עוברות ממאפיין style למאפיין class בצורה קצת יצירתית:
function App() {
  return (
    <div className="App p-4">
      <h1>My Cool Game</h1>
      <ul className="flex p-0 flex-col">
        <li className="my-4"><MenuButton>Single Player</MenuButton></li>
        <li className="my-4"><MenuButton>Multi Player</MenuButton></li>
      </ul>
    </div>
  )
}

function MenuButton({ href="#", children }) {
  return (
    <a className="inline-block py-4 px-6 text-white w-40 bg-[#4CAF50]" href={href}>{children}</a>
  );
}

export default App
היתרונות בקצרה: 1. הגדרות העיצוב קצרות יותר. 2. אני לא מגדיר גדלים שרירותיים אלא משתמש במספרים של Tailwind כמו p-4 שמציין padding בגודל 1rem. וכן אפשר לבחור את המשמעות של p-4 בקובץ קונפיגורציה אם אתם צריכים גדלים שונים. 3. אין בעיה להשתמש בערכים שרירותיים עם סוגריים מרובעים, כמו שעשיתי עם צבע הרקע, אבל גם שם אפשר להגדיר בקונפיגורציה שם לצבע הזה ואז להשתמש בקלאס שיתאים לו. 4. כל הגדרות העיצוב מופיעות בקלאסים, כך שיחסית קל לשלב את הקומפוננטה במקומות אחרים ולדרוס את העיצוב באמצעות שילוב קובץ CSS חיצוני. 5. ל Tailwind יש תמיכה מלאה ב Media Queries, hover וכל מה שתרצו. את שמות הקלאסים אפשר למצוא בתיעוד של טיילווינד, ובתחילת העבודה איתו כדאי להיעזר באתר transform.tools כדי להפוך כל קוד CSS לרשימת קלאסים של Tailwind. קישור לכלי: https://transform.tools/css-to-tailwind ## איך לשלב טיילווינד ביישום שלכם בשביל לבנות פרויקט ריאקט עם Tailwind CSS בסך הכל צריך ללכת לפי ההוראות באתר שלהם בקישור https://tailwindcss.com/docs/guides/vite. אני מדביק פה את התקציר שיהיה לנו:
$ npm create vite@latest my-project -- --template react
$ cd my-project
$ npm install -D tailwindcss postcss autoprefixer
$ npx tailwindcss init -p
אחרי זה צריך לעדכן קובץ קונפיגורציה בשם tailwind.config.cjs כדי שיכיל את התוכן הבא:

ToCode
1 420
# קוד לשימוש חוזר אחת הבעיות שאני מוצא יותר מדי במערכות היא האתגר של שימוש חוזר בקוד, וזה הולך ככה - מצד אחד אנחנו רוצים לכתוב קוד שאפשר יהיה להשתמש בו שוב ושוב, אבל מצד שני אנחנו (כרגע) עסוקים מדי בשביל לעשות את העבודה הקשה של לכתוב קוד לשימוש חוזר. התוצאה היא קוד שנראה כמו קוד שמתאים לשימוש חוזר, אבל כשאתה בא להשתמש בו בעוד מקום אתה מגלה שיש יותר מדי תלויות. במקרה הטוב תפסת את זה בזמן וכתבת הכל מחדש, במקרה הרע תפסת את זה מאוחר מדי ואתה תקוע בבוץ מנסה לארגן מחדש את הקוד "לשימוש חוזר" וצריך לפתור עכשיו באגים בשני פיצ'רים במקום רק בפיצ'ר החדש שאתה בונה. דוגמה? בשמחה. קחו קוד פייתון שעושה יותר מדי עבודה על משפט:
import re

text = "one two three I want to see three four five I am alive"

# delete all spaces in sentences
text = re.sub(r'\s+', '', text)

# replace each letter by its ordinal value
text = [ord(c) for c in text]

# leave only values that are larger than what came before
new_text = [text[0]]
for value in text:
    if value > new_text[-1]:
        new_text.append(value)

text = new_text

print(sum(text))
לקוד יש שלושה חלקים שבנויים לעבוד אחד אחרי השני עם הערה שמתארת מה עושה כל חלק. לא בטוח שכולם נכונים אבל לפחות הקוד סגור כולו באותו מקום, ואף אחד לא יחלום להשתמש בו בעוד מקום במערכת. במקרה הגרוע יצטרכו לקרוא את הקוד וממש להעתיק את השורות שעושות עבודה דומה. גירסה מסוכנת יותר של הקוד הזה עשויה להיראות כך:
import re

def main():
    text = "one two three I want to see three four five I am alive"
    text = delete_all_spaces(text)
    text = change_to_ord_values(text)
    text = change_to_increasing(text)
    print_result(text)


def delete_all_spaces(text):
    # delete all spaces in sentences
    text = re.sub(r'\s+', '', text)
    return text

def change_to_ord_values(text):
    # replace each letter by its ordinal value
    text = [ord(c) for c in text]
    return text

def change_to_increasing(text):
    # leave only values that are larger than what came before
    new_text = [text[0]]
    for value in text:
        if value > new_text[-1]:
            new_text.append(value)

    text = new_text
    return text

def print_result(text):
    print(sum(text))


main()
איזה יופי נכון? הכל מסודר עכשיו בפונקציות, כל פונקציה מקבלת את הקלט שלה ומחזירה פלט והתוכנית מחזירה את אותה תוצאה. עכשיו אם נרצה להשתמש רק בחלק מהקוד למקום אחר נוכל - נכון? רוב הסיכויים שלא. הפונקציות לא מספיק גנריות בשביל שיהיו יעילות במקום אחר, כתבתי אותן פשוט בגלל שזה מה שהיה לי מול העיניים והחלוקה שרירותית לגמרי. הרבה יותר סביר שמישהו ינסה להשתמש באחת מהן, יבין שאין לו בדיוק קלט מהסוג שהפונקציה צריכה ואז ייתקע בארגון מחדש של הקוד. אז כן אם יש לכם זמן לכתוב קוד גנרי, בארכיטקטורה נכונה שקל להשתמש בו בעוד מקומות ואתם יודעים לתאר מקומות נוספים במערכת שיצטרכו אותו - לכו על זה וכתבו את הקוד הגנרי. אבל שווה להיזהר מקוד שרק נראה גנרי, ושיישבר בשניה שננסה לשלב אותו בעוד מקום.