• בלוג
  • שלוש דוגמאות לתיעוד טוב מפרויקטי קוד פתוח

שלוש דוגמאות לתיעוד טוב מפרויקטי קוד פתוח

07/08/2022

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

1. תיעוד Redux

הפונקציה bindActionCreators של רידאקס היא דרך קלה להפוך את ה Action Creators שלנו לקלים יותר לשימוש. הנה קוד התיעוד שלה מתוך הקובץ src/bindActionCreators.ts בפרויקט:


/**
 * Turns an object whose values are action creators, into an object with the
 * same keys, but with every function wrapped into a `dispatch` call so they
 * may be invoked directly. This is just a convenience method, as you can call
 * `store.dispatch(MyActionCreators.doSomething())` yourself just fine.
 *
 * For convenience, you can also pass an action creator as the first argument,
 * and get a dispatch wrapped function in return.
 *
 * @param actionCreators An object whose values are action
 * creator functions. One handy way to obtain it is to use ES6 `import * as`
 * syntax. You may also pass a single function.
 *
 * @param dispatch The `dispatch` function available on your Redux
 * store.
 *
 * @returns The object mimicking the original object, but with
 * every action creator wrapped into the `dispatch` call. If you passed a
 * function as `actionCreators`, the return value will also be a single
 * function.
 */
export default function bindActionCreators<A, C extends ActionCreator<A>>(

קישור לקוד המקורי: https://github.com/reduxjs/redux/blob/master/src/bindActionCreators.ts#L18

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

2. תיעוד Requests

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

הנה הפונקציה merge_setting מתוך הקובץ requests/sessions.py:

def merge_setting(request_setting, session_setting, dict_class=OrderedDict):
    """Determines appropriate setting for a given request, taking into account
    the explicit setting on that request, and the setting in the session. If a
    setting is a dictionary, they will be merged together using `dict_class`
    """

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

אותה פונקציה כוללת גם הערה חשובה בתוך הקוד:

    # Remove keys that are set to None. Extract keys first to avoid altering
    # the dictionary during iteration.
    none_keys = [k for (k, v) in merged_setting.items() if v is None]
    for key in none_keys:
        del merged_setting[key]

קישור לקוד: https://github.com/psf/requests/blob/main/requests/sessions.py.

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

3. תיעוד lodash

והנה דוגמה אחרונה מספריית lodash, שקריאה בתיעוד שלה היא תמיד חוויה משמחת:

/**
 * Creates an object composed of keys generated from the results of running
 * each element of `collection` thru `iteratee`. The corresponding value of
 * each key is the number of times the key was returned by `iteratee`. The
 * iteratee is invoked with one argument: (value).
 *
 * @since 0.5.0
 * @category Collection
 * @param {Array|Object} collection The collection to iterate over.
 * @param {Function} iteratee The iteratee to transform keys.
 * @returns {Object} Returns the composed aggregate object.
 * @example
 *
 * const users = [
 *   { 'user': 'barney', 'active': true },
 *   { 'user': 'betty', 'active': true },
 *   { 'user': 'fred', 'active': false }
 * ]
 *
 * countBy(users, value => value.active);
 * // => { 'true': 2, 'false': 1 }
 */
function countBy(collection, iteratee) {

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

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