הבלוג של ינון פרק

טיפים קצרים וחדשות למתכנתים

איך להפוך למתכנתי פייתון טובים יותר

13/02/2023

נתחיל במה שלא עובד (וזה קצת מפתיע). אפשר לדמיין שבשביל להיות מתכנתי פייתון טובים יותר עלינו ללמוד כמה שיותר APIs של פייתון. למשל אם נלמד גם לכתוב ממשקים גרפיים, גם לקבל מידע מהרשת, גם לנתח מידע וגם לבנות בינה מלאכותית אז כל API חדש יהפוך אותנו למתכנתי פייתון טובים יותר.

אבל זה לא ממש עובד.

הדבר היחיד שאנחנו מקבלים מללמוד כל חודש API חדש לגמרי הוא סחרחורת. אנחנו מקבלים את התחושה שהטכנולוגיה רצה מהר מדי, שאי אפשר אף פעם לדעת הכל, שלא משנה כמה אני אלמד תמיד אצטרך "להתחיל מחדש" ושהעולם הזה לא בשבילי. אנחנו כל פעם משקיעים בדברים שלא נשארים, ואחרי חודש או שנה מגלים שחזרנו לנקודת ההתחלה.

דרך טובה יותר להשתפר בתור מפתחי פייתון, או כל טכנולוגיה אחרת, היא להשקיע בדברים שנשארים. בפייתון זה יהיה:

  1. להבין איך פייתון מבינה ומריצה את התוכנית שלי.

  2. להבין איך להשתמש ב Type Hints בצורה שתייצר ערך ותהפוך את הקוד לקל יותר לקריאה.

  3. להבין איך לעשות דברים במקביל, באמצעות Threads, Processes או async.

  4. להבין מה זה תכנות מונחה עצמים ואיך למדל באמצעותו בעיות.

  5. להבין איך לכתוב בדיקות בצורה יעילה ומהירה.

  6. להבין מה משפיע על זמן הריצה של תוכנית ואיך למדוד ולשפר זמני ריצה.

  7. להכיר את המבנים היותר בסיסיים של פייתון כמו Decorator ו Metaclass ואיזה תפקיד הם משחקים בספריות קוד מרכזיות.

  8. להבין את הקשר בין Python ל C. אחרי זה איך לכתוב הרחבות ב C לפייתון, ואיזה חבילות פייתון מרכזיות משתמשות בהרחבות C כדי לשפר ביצועים.

כשיש ספק, השקיעו בדברים שנשארים.

[ריאקט] זהירות! מידע גלובאלי בין בדיקות

12/02/2023

נתבונן בקומפוננטה הבאה מתוך דף הפתיחה של swr:

import useSWR from 'swr'

function Profile () {
  const { data, error, isLoading } = useSWR('/api/user/123', fetcher)

  if (error) return <div>failed to load</div>
  if (isLoading) return <div>loading...</div>

  // render data
  return <div>hello {data.name}!</div>
}

ונכתוב תוכנית בדיקה שמשנה את fetch כדי להחזיר אוביקט שלנו, בשביל שאפשר יהיה לבדוק את הקומפוננטה גם בלי לצאת לרשת:


import { render, screen } from '@testing-library/react';
import Profile from './Profile;

test('test one', async () => {
  jest.spyOn(global, 'fetch').mockImplementation(url => Promise.resolve({
    json: () => Promise.resolve({ name: 'bug'})
  }));

  render(<Profile />);
  expect(await screen.findByText(/hello bug/));
});

קודם כל תשמחו לשמוע שהתוכנית עובדת. עכשיו קראו את הקוד שוב. רואים את הבעיה?

המשך קריאה

[ריילס] היום למדתי: לא מעבירים מזהים למודל ביצירה או עדכון

11/02/2023

את הטיפ הבא מצאתי במאמר הזה של חברת Betterment ומיד התחברתי אז אני משתף גם פה.

נתחיל עם קוד ריילס הבא עבור controller:

class Documents::AttachmentsController < ApplicationController
    def create
        AttachmentLink.new(create_params.merge(document: document)).save!
    end

    private

    def create_params
        params.permit(:attachment_id, :caption)
    end

    def document
        current_user.documents.find(params[:document_id])
    end
end

הקוד מאפשר להצמיד Attachments למסמך דרך הפונקציה create. הפונקציה מקבלת מהדפדפן מזהה של "קובץ מצורף" ומזהה של "מסמך" ויוצרת AttachmentLink שזה אוביקט חיבור בין השניים.

קחו רגע לקרוא את הקוד ונסו לחשוב מה שבור בו.

המשך קריאה

חדש באתר: מיני קורס בדיקות בריאקט

10/02/2023

זה לא פשוט לכתוב בדיקות, במיוחד בדיקות לקוד צד לקוח.

הרבה אנשים חושבים שבדיקות לוקחות להם זמן והם מעדיפים להשקיע את הזמן הזה בבניית פיצ׳רים חדשים. הם שוכחים שהדבר הכי מרגיז במערכות זה כשמעלים גירסה חדשה וכל הדברים הישנים מפסיקים לעבוד.

אנשים אחרים חושבים שבדיקות End To End הן חשובות, אבל על בדיקות יחידה אפשר לוותר. הם נכוו מספיק פעמים מבדיקות יחידה לא יעילות שלא מצאו את הבאגים האמיתיים במערכת.

ויש גם את מי שבטוחים שבדיקות זה רעיון ממש מצוין אבל ספציפית המערכת שלהם מסובכת מדי וכרגע יש יותר מדי באגים. יום אחד כשדברים קצת יתייצבו הם ישמחו לכתוב בדיקות.

אם גם אתם חלק מאחת הקבוצות ברשימה שלמעלה תרגישו חופשי להמשיך הלאה, הפוסט הזה לא בשבילכם.

מצד שני אם אתם היום מרגישים לא בטוחים לגבי הקוד שלכם. אם אתם מבינים שאי אפשר להמשיך כך כשכל ריפקטור קטן שובר אינסוף דברים. אם גם לכם נמאס לעבור בין הדפדפן ל VS Code כל הזמן בשביל לבדוק שהקוד שאתם כותבים עובד כמו שצריך, אז תשמחו לשמוע שהעליתי היום מיני קורס חדש במטרה לעזור לכם להתחיל לכתוב בדיקות יחידה או לשפר את רמת הבדיקות שאתם כותבים.

הקורס כולל 6 סרטי וידאו באורך כולל של קצת פחות משעה, ועוסק בנושאים:

  1. היכרות עם react-testing-library, איך נראית תוכנית בדיקה ואיך לא לבזבז זמן בכתיבת תוכניות הבדיקה.

  2. בדיקת טיפול באירועים עם user-event.

  3. בדיקת קומפוננטות המושפעות מזמן, גם בצורה אסינכרונית וגם באמצעות שעונים מזויפים.

  4. בדיקת תקשורת באמצעות mock לפונקציית fetch ו Best Practices סביב בדיקות כאלה.

  5. בדיקת קומפוננטות-בתוך-קומפוננטות באמצעות Jest Spies.

  6. בדיקת יישומים המשתמשים ב Redux.

השיעורים מעשיים, כל שיעור מודגם על קומפוננטות אמיתיות שכתובות ב React ו TypeScript וייתן לכם בסיס טוב לבניית בדיקות למערכות שלכם.

אם יש לכם מנוי לאתר יכולים כבר להיכנס ולצפות בקישור: https://www.tocode.co.il/boosters/7

ואם עדיין אין לכם מנוי היום הוא הזדמנות מצוינת להירשם. פשוט לחצו על הקישור לקורס כדי להגיע לדף ההרשמה.

הצילו! ספריית הקוד הפתוח שאני צריך כבר לא מתוחזקת

08/02/2023

בעולם של היום הדבר הכי קל בפרויקט זה להוסיף תלות בעוד ספריית קוד פתוח. צריכה הצפנה? יש מודול ב pip שעושה את זה. צריך לפתוח קובץ זיפ? פשוט תריץ npm install. לפעמים נדמה לי שחצי מהעבודה שלנו היא למצוא את המודול המתאים בשביל לפתור את הבעיה.

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

ואם זה גם המקרה שלכם הנה כמה רעיונות פרקטיים שאפשר לנסות:

  1. להתעלם - זוכרים את "אם זה עובד לא נוגעים"? אז הרבה פעמים אנחנו רואים פרויקטים שתלויים בספריות קוד פתוח שכבר שנים לא מתוחזקות, ולאף אחד לא אכפת. בדרך כלל בפרויקטים כאלה יהיו כל מיני כללים מוזרים כמו "אסור לשדרג את node כי אז כלום לא יעבוד", וכולם מתכננים יום אחד לשדרג את התלות הלא מתוחזקת אבל לא מגיעים לזה.

  2. ליצור fork מתוחזק - כש kreeti שמו לב שספריית הריילס האהובה עליהם ננטשה, הם יצרו פורט מתוחזק. וכן יש אפילו כלי שעוזר למצוא פורקים קיימים לספריות שננטשו.

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

האפשרות השלישית היא המועדפת עליי. למרות שאפשר להישאר עם פיתרונות ישנים ולגרום לכל דבר לעבוד, אין כמו הכיף בלעבוד על פרויקט שהכל בו חדש, ושאפשר בקלות למצוא תיעוד רלוונטי ולשלב יכולות חדשות או כלים חדשים בלי התנגשויות מוזרות.

למה למחוק קוד?

07/02/2023

אודי הרשקוביץ כתב בסיפור על הבראנץ הנדיב-

ומנהל הפיתוח גייס עוד טאלנטים והוסיף המון באזז-וורדס ל Branch, והקוד הלך והסתבך ונהיה כבד ומסורבל, ואף אחד כבר לא ידע בדיוק מה עושים ולמה, אבל הדברים איכשהו עבדו. והמשקיעים ראו רק את הצמיחה האדירה של ה Branch, והמשיכו להזרים כסף. ולמרות הבלאגן והספגטי התוכנה המשיכה לעבוד איכשהו, וה Branch היה מקומפל.

קוד עובד הוא משקולת. הוא יוצר אילוצים, הוא יוצר בעיות קומפילציה כשמנסים להזיז דברים ממקום למקום, הוא מונע Refactoring כי אי אפשר לדעת איך זה ישפיע על הדברים הקיימים. זה לא סתם שמתכנתים הכי אוהבים פרויקטי Green Fields. כשאין קוד אין מה שיישבר.

ולמחוק קוד? למחוק קוד זאת הדרך הכי קלה להאיץ את הפיתוח. זה להקטין את כדור הבוץ הענק שאנחנו קוראים לו מערכת. כדי שהבראנץ שלנו לא יגמור כמו הבראנץ הנדיב.

אילוצים פיקטיביים

06/02/2023

"שימו לב כולם היום נכתוב ממשק גרפי מבוסס ווב למחשבון שרק יודע לחבר מספרים. הממשק צריך לאפשר למשתמש להקליד מספרים וכשלוחצים על כפתור צריך להציג את סכום כל המספרים שהוקלדו."

"רגע בוס אין פה מספיק אינפורמציה" נשמע קול מתכנתי מקצה החדר, "מה גבולות הגיזרה? בכמה מספרים צריך לתמוך?"

"אני לא יודע תזכור שזו פעולת חיבור אז בטח לא יהיה פחות מ-1, וגם יותר מדי שורות לא נראה יפה על המסך אז בואו נגביל את זה ל-10 מספרים במקסימום".

וכך נולד אילוץ פיקטיבי. מכאן כל מיני מקומות בקוד יכולים להתחיל להתבסס על זה ש"אף פעם לא יהיו יותר מ 10 תיבות", ולכתוב פונקציות כמו זו למשל:

function update() {
  var i;
  for (i = 3; i <= 10; i++) {
    labelID = "line_R" + i;
    if (document.getElementById("num").value >= i) {
      document.getElementById(labelID).style.display = "list-item";
    } else {
      document.getElementById(labelID).style.display = "none";
    }
  }
}

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

העלות של קיצורי דרך (ולמה כל כך מסובך לכתוב gitignore)

05/02/2023

ניסיתי לבקש השבוע מ gitignored ליצור לי קובץ gitignore לפרויקט פייתון. זאת היתה ההצעה שלו:

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
.pybuilder/
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
#   For a library or package, you might want to ignore these files since the code is
#   intended to run in multiple environments; otherwise, check them in:
# .python-version

# pipenv
#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
#   However, in case of collaboration, if having platform-specific dependencies or dependencies
#   having no cross-platform support, pipenv may install dependencies that don't work, or not
#   install all needed dependencies.
#Pipfile.lock

# poetry
#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
#   This is especially recommended for binary packages to ensure reproducibility, and is more
#   commonly ignored for libraries.
#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock

# pdm
#   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
#   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
#   in version control.
#   https://pdm.fming.dev/#use-with-ide
.pdm.toml

# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# Cython debug symbols
cython_debug/

# PyCharm
#  JetBrains specific template is maintained in a separate JetBrains.gitignore that can
#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
#  and can be added to the global gitignore or merged into this file.  For a more nuclear
#  option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

אחסוך לכם את הספירה - יש פה 160 שורות. מאה ושישים. אני מנחש שרק חלק קטן מהאנשים שישתמשו בתבנית כזאת באמת יקראו את כל השורות כדי לחשוב מה רלוונטי ומה לא. ובדומה לקיצורי דרך אחרים גם פה יש Trade Off.

במקרה של gitignore האיזון הוא בין "להסתכן שמשהו לא חשוב ייכנס לגיט" לבין "להסתכן שמשהו חשוב לא ייכנס לגיט". הטמפלייט החליט שאת תיקיית site אני שומר מחוץ לגיט כי הוא חושב שהיא רלוונטית לפרויקט מסוג מסוים, אבל אולי דווקא בפרויקט שלי אני כן צריך את התיקיה הזאת. מצד שני אם הייתי כותב את gitignore ידנית ומוסיף שורה כל פעם שאני רוצה להתעלם מעוד קובץ, אז כנראה מדי פעם הייתי צריך לתקן קומיטים שהכניסו בטעות קבצים שלא צריכים להיות שם.

בפיתוח תוכנה הסיטואציה היא אף פעם לא "כמה שיותר יותר טוב"; לכל בחירה יש עלות, וכדאי לחפש אותה לפני השימוש בכלי החדש.