fa
Feedback
ToCode

ToCode

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

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

نمایش بیشتر
1 418
مشترکین
-224 ساعت
-37 روز
-630 روز
آرشیو پست ها
ToCode
1 418
אנחנו יותר חכמים היום כשדברים שבורים במערכת יש נטייה להשקיע פחות גם במנגנונים החדשים. בשביל מה לבנות את הדבר החדש נכון כשיש לי כל כך הרבה חלונות שבורים ? עדיף כבר שיעבוד. כשהמצב כל כך גרוע שיפור קטן, גם אם לא מבוצע בדיוק כמו שצריך יהיה הרי צעד בכיוון הנכון. כשהמחשבות האלה מגיעות אני מזכיר לעצמי את המשפט "אנחנו יותר חכמים היום". נכון, לפני 5 שנים בנינו ארכיטקטורה שלא היתה משהו, ונכון עכשיו אנחנו רואים את כל הבעיות שלה, ונכון אפשר לסתום את החור בצנרת וזה יחזיק עד הפיצוץ הבא, אבל אם כבר מתקנים שווה להשקיע את המאמץ ולתקן נכון. זה לא יפתור את כל בעיות העולם אבל תיקון נכון ועוד תיקון נכון לאט לאט מזיזים את הספינה לכיוון טוב יותר. אנחנו יותר חכמים היום. מותר לבנות דברים טוב יותר.

ToCode
1 418
איך להגיע למידע הנכון בחמישה צעדים כשמנסים ללמוד טכנולוגיה חדשה אנחנו נכנסים לעולם חדש ומופלא, אבל הבעיה בעולם הזה היא שאין באמת מדריך, או יותר נכון מרוב תיעוד לא רואים את הדרך הנכונה ביותר עבורנו. זה המסלול שאני אוהב לקחת כדי לא ללכת לאיבוד בתיעוד כשהפריימוורק יותר מדי גדול. מה בעצם הבעיה בספריות קטנות מספיק לקרוא 10 דקות את מדריך ה "Getting Started" בשביל להבין מי נגד מי. אם הספריה חדשה אז בכלל שמח כי יש רק דרך אחת לעשות דברים, אנשים לא כתבו עליה יותר מדי ולא מצאו טריקים ודרכים לרמות את המערכת. ככל שהספריה גדולה וותיקה יותר העניינים מסתבכים. כל API שהשתנה אומר שאנחנו הולכים למצוא ברשת תיעוד גם על ה API הישן וגם על החדש, ואם הגירסה הישנה עדיין נתמכת יהיו לנו התלבטויות איך לעשות דברים ואם צריך בכלל להשתמש בגירסה החדשה של ה API. כשריאקט עברו לעבוד עם Hooks עדיין הרבה מהתיעוד הכיל דוגמאות של קלאסים. ויו מכיל עדיין הרבה דוגמאות של Options API כולל באתר הרשמי, ועל ריילס אני לא רוצה בכלל להתחיל לדבר. לצערנו AI רק מחמירים את הבעיה כי הם למדו דרך האינטרנט ולכן הם מכירים את כל התחבירים אבל לא תמיד יודעים לבחור בשבילנו את התחביר שנכון ל Use Case שאנחנו צריכים. במיוחד בתחילת הדרך הסכנה בלימוד הדבר הלא נכון היא גדולה. מתחילים באתר התיעוד הרשמי. הצעד הראשון שלי בלימוד טכנולוגיה חדשה הוא האתר הרשמי של אותה ספריה. זה יכול להיות דף הגיטהאב שלהם או אתר שהם בנו. הכי טוב אם יש בו מדריך למשתמש או דף Getting Started. המטרה להוציא מכאן כמה שיותר מידע אבל לא לטבוע. אם תלכו למדריך הרשמי של ריילס תמצאו אינסוף חומר ולכן לא הגיוני לקרוא את כל המדריכים כשאתם רק מתחילים, במקום זה תוכלו להתחיל עם דף ה Getting Started שלהם מתוך הבנה שעוד תחזרו לשאר המדריכים בהמשך. נעזרים ב Chat GPT כדי לבנות דברים, מתוך הבנה שהם לא יהיו טובים. אחרי שלמדנו את הבסיס שצריך בשביל לכתוב דברים הגיע הזמן לחקור. פה ה AI הוא חבר טוב אפשר להיעזר ב Chat GPT, קלוד או דיפסיק או כל חבר אחר שלכם כדי להתחיל לכתוב קוד, לקבל תשובות במה להשתמש ולקבל Code Review על הקוד שכתבתם. אחרי שיש לכם כמה דברים שעובדים תוכלו לחזור למדריכים הרשמיים כדי ללמוד יותר, ומכאן מתחיל פינג-פונג בין בנייה לקריאה. מחפשים אם יש אנשים אחרים שבנו דברים דומים ומוכנים לספר על זה. אחרי שיש לכם הבנה מסוימת של הספריה ובניתם כמה דברים יהיה מעניין למצוא אנשים אחרים שבנו דברים דומים ומוכנים לספר על זה. בניתם משחק סנייק ב pygame? מעולה, עכשיו תחפשו פוסטים ברשת של אנשים שבנו את אותו משחק בפייגיים ותראו מה הם עשו דומה או שונה מכם. כתבתם גרפים כדי להבין data בעזרת echarts? מצוין עכשיו לכו לחפש אנשים אחרים שניתחו מידע באמצעות ספריה זו ותבינו מה הם עשו דומה או שונה מכם. בנקודה הזאת אנחנו עדיין לא באים כדי לשפוט אלא כדי ללמוד, אנחנו מוכנים לקבל שטעינו אם נמצא רעיון שנראה טוב יותר ברשת אבל מספיק בטוחים בעצמנו כדי לא לבלוע כל רעיון או הצעה. מחפשים Style Guides ו Best Practices. אחרי שראינו כמה דרכים ונעזרנו ב AI כדי להבין טוב יותר את ההבדלים בין הפיתרונות אנחנו במקום טוב כדי לחפש מדריכי Best Practices רשמיים על הטכנולוגיה. במדריכים אלה נמצא בדיוק את ההתלבטויות שהיו לנו עם עצות פרקטיות איזה שיטה טובה יותר ולמה. מקשיבים לאנשים שבפנים. אחרי שגיבשנו דעה, בנינו כמה דברים ועברנו על כל התיעוד הרשמי מגיע הזמן לחפש אנשים מבפנים. לא בגלל שהם יודעים הכי טוב איך צריך להשתמש במוצר שלהם (לפעמים הם יודעים, לפעמים השיטה שלהם פחות מתאימה לנו), אלא בגלל שהם מבינים את המוצר הכי טוב ולכן הבנה של התפיסה שלהם תעזור לנו לחדד את ההבנה שלנו. מתכנתי ריילס מקצועיים הקשיבו ל DHH מספר על פיתוח תוכנה. אולי לא הסכמנו איתו אבל אנחנו מבינים את תפיסת העולם שלו ומבינים איך זה משפיע על הכיוון של ריילס. כשמקשיבים לגיירמו ראוך מבינים לאן ורסל הולכת והבלוג של דן אברמוב הוא עדיין מקום מאוד חשוב למי שרוצה להבין את הכיוון אליו ריאקט הולכת.

ToCode
1 418
לאחר מכן אני יכול להסתכל מה נוצר עם הפקודה:
$ awslocal cloudformation describe-stacks \
  --stack-name my-sqs-lambda-stack-2 
הפלט אצלי הוא:
{
    "Stacks": [
        {
            "StackId": "arn:aws:cloudformation:us-east-1:000000000000:stack/my-sqs-lambda-stack-2/512113f2",
            "StackName": "my-sqs-lambda-stack-2",
            "Description": "CloudFormation template for SQS queue with Lambda consumer",
            "CreationTime": "2025-02-07T13:12:29.585000+00:00",
            "LastUpdatedTime": "2025-02-07T13:12:29.585000+00:00",
            "RollbackConfiguration": {},
            "StackStatus": "CREATE_COMPLETE",
            "DisableRollback": false,
            "NotificationARNs": [],
            "Capabilities": [
                "CAPABILITY_IAM"
            ],
            "Outputs": [
                {
                    "OutputKey": "QueueURL",
                    "OutputValue": "http://sqs.us-east-1.localhost.localstack.cloud:4566/000000000000/my-sqs-lambda-stack-2-MyQueue-0becc311",
                    "Description": "URL of the SQS Queue"
                },
                {
                    "OutputKey": "QueueARN",
                    "OutputValue": "arn:aws:sqs:us-east-1:000000000000:my-sqs-lambda-stack-2-MyQueue-0becc311",
                    "Description": "ARN of the SQS Queue"
                },
                {
                    "OutputKey": "FunctionARN",
                    "OutputValue": "arn:aws:lambda:us-east-1:000000000000:function:my-sqs-lambda-stack-2-MyFunction",
                    "Description": "ARN of the Lambda Function"
                }
            ],
            "Tags": [],
            "EnableTerminationProtection": false,
            "DriftInformation": {
                "StackDriftStatus": "NOT_CHECKED"
            }
        }
    ]
}
וכמובן מה שהכי מעניין שם זה ה Outputs שם אני רואה את הפרטים של התור והפונקציה שנוצרו. אני שולח הודעה לתור עם:
$ awslocal sqs send-message \
  --queue-url "http://sqs.us-east-1.localhost.localstack.cloud:4566/000000000000/my-sqs-lambda-stack-2-MyQueue-0becc311" \
  --message-body '{"num1": "10", "num2": "10"}'
ומסיים בחיפוש פלט הפונקציה בלוג. בשביל זה מפעילים:
$ awslocal logs describe-log-groups
{
    "logGroups": [
        {
            "logGroupName": "/aws/lambda/my-sqs-lambda-stack-2-MyFunction",
            "creationTime": 1738933949640,
            "metricFilterCount": 0,
            "arn": "arn:aws:logs:us-east-1:000000000000:log-group:/aws/lambda/my-sqs-lambda-stack-2-MyFunction:*",
            "storedBytes": 12439
        }
    ]
}
ממנו אנחנו לומדים שבלוג יש קבוצה אחת ורואים את השם שלה. אחרי זה עם שם הקבוצה אני מפעיל את הפקודה הבאה כדי לראות את כל הלוגים של היומיים האחרונים:
$ awslocal logs tail /aws/lambda/my-sqs-lambda-stack-2-MyFunction --since 2d

ToCode
1 418
ניסוי localstack: הקמת תור ופונקציה לטיפול בהודעות לוקאלסטאק הוא כלי להרצת הרבה סרביסים של AWS בצורה מקומית. הוא מצוין לפיתוח או בדיקות ואולי גם לפרודקשן כשרוצים לחסוך בעלויות. בשביל להבין איך הוא עובד בניסוי היום אני בונה תור הודעות על SQS ופונקציית lambda, עם מיפוי ביניהם שגורם לפונקציה לרוץ אוטומטית כשנכנסת הודעה חדשה לתור. קוד הפונקציה בשביל המשחק הפונקציה לא צריכה לעשות הרבה. פונקציה שמופעלת בתגובה להודעה בתור מקבלת פרמטר event שמייצג את האירוע ובו יש שדה בשם Records שמכיל את ההודעות מאירוע זה (הפעלה אחת יכולה לכלול מנה של מספר הודעות). ההודעות יהיו אוביקטי JSON עם שדות num1 ו num2 והפונקציה תדפיס את מכפלת המספרים ללוג. זה הקוד בקובץ index.js:
function handleRecord(body) {
  const product = body.num1 * body.num2;
  console.log(body);
  const response = {
    statusCode: 200,
    body: "The product of " + body.num1 + " and " + body.num2 + " is " + product,
  };
  console.log(response);
  return response;
}

exports.handler = async (event) => {
  try {
    for (const record of event.Records) {
      console.log(\record = \);
      console.log(record);
      handleRecord(JSON.parse(record.body));
    }
  } catch (_err) {
    console.log(\Error parsing body. Body was: ${event.body}\);
  }
};
אחרי שיש לי את הקובץ אני שומר אותו לקובץ zip עם הפקודה:
$ zip function.zip index.js
ואנחנו מוכנים להמשיך. יצירת הסטאק אפשר ליצור אוביקטים על AWS אחד אחד או באמצעות סקריפט CloudFormation שיוצר את כל הסטאק הטכנולוגי. אני משתמש בסקריפט ולכן יוצר את הקובץ stack.yml עם התוכן הבא:
AWSTemplateFormatVersion: '2010-09-09'
Description: 'CloudFormation template for SQS queue with Lambda consumer'

Resources:
  # SQS Queue
  MyQueue:
    Type: AWS::SQS::Queue
    Properties:
      VisibilityTimeout: 30

  # CloudWatch Log Group
  MyFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub '/aws/lambda/${AWS::StackName}-MyFunction'
      RetentionInDays: 14

  # Lambda Function
  MyFunction:
    Type: AWS::Lambda::Function
    Properties:
      Code: 
        ZipFile: function.zip
      Handler: index.handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Runtime: nodejs18.x
      Timeout: 10
      FunctionName: !Sub '${AWS::StackName}-MyFunction'
      Environment:
        Variables:
          QUEUE_URL: !Ref MyQueue

  # Lambda Execution Role
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
        - PolicyName: SQSAccess
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - sqs:ReceiveMessage
                  - sqs:DeleteMessage
                  - sqs:GetQueueAttributes
                Resource: !GetAtt MyQueue.Arn

  # Event Source Mapping to connect SQS to Lambda
  MyEventSourceMapping:
    Type: AWS::Lambda::EventSourceMapping
    Properties:
      BatchSize: 1
      Enabled: true
      EventSourceArn: !GetAtt MyQueue.Arn
      FunctionName: !Ref MyFunction

Outputs:
  QueueURL:
    Description: URL of the SQS Queue
    Value: !Ref MyQueue
  QueueARN:
    Description: ARN of the SQS Queue
    Value: !GetAtt MyQueue.Arn
  FunctionARN:
    Description: ARN of the Lambda Function
    Value: !GetAtt MyFunction.Arn
הסטאק יוצר תור, פונקציה ו EventSourceMapping שזה החיבור ביניהם. גודל המנה שם הוא 1 ולכן במערך ההודעות של הפונקציה נראה תמיד רק רשומה אחת. הפעלה ובדיקה בשביל ליצור את הסטאק אני מריץ:
$ awslocal cloudformation create-stack \
  --stack-name my-sqs-lambda-stack-2 \
  --template-body file://stack.yml \
  --capabilities CAPABILITY_IAM

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

ToCode
1 418
מתי כדאי לעשות Rewrite לזרוק ולכתוב מחדש את כל המערכת?! השתגעת? כן יש לנו בעיות אבל בטח המצב לא כזה חמור... נכון? אין ספק שלזרוק את כל הקוד ולבנות גירסה חדשה מאפס זה אתגר רציני עם סיכונים משמעותיים וכמעט תמיד עדיף לתקן את המוצר הקיים, ובכל זאת יש מצבים בהם זה יכול להשתלם, לדוגמה (מהקל אל הקשה): 1. כשאין משתמשים - הכי קל, אף אחד לא יתגעגע למוצר. 2. כשיש משתמשים אבל אין צורך בתחזוקה כי אין להם דרישות - ואז אפשר להקפיא את העבודה על המערכת הקיימת ולהעביר את כל המשאבים לשכתוב. 3. כשיש משתמשים ויש להם דרישות, אבל כל ניסיון שלנו להתקדם שובר משהו אחר וזמן הפיתוח לדברים מאוד בסיסיים לא מאפשר לעזור לאף אחד. 4. כשיצאה טכנולוגיה חדשה ומהפכנית שמאפשרת לבנות מוצר שפותר את אותה בעיה בצורה טובה בהרבה (היוש Chat GPT). 5. כשהתחזוקה של המערכת עולה יותר ממה שהמערכת מכניסה, ואין צפי לגידול מפתיע בהכנסות. ולסיום שאלה - באיזה מצבים אתם זורקים את כל הקוד ומתחילים מחדש? קרה לכם שהשכתוב הצליח טוב יותר מהמוצר המקורי? גרוע יותר? לא הסתיים מעולם?

ToCode
1 418
תזרוק עליו נעליים כבני אדם אנחנו אוהבים לפתור בעיות, וכמו Chat GPT גם אצלנו כשלא מבינים את הבעיה מתחילים להמציא: 1. נסה למחוק את ה node_modules ולהתקין מחדש. 2. תעשה ריסטרט לשרת. 3. תחזור לגירסה ישנה של הפרויקט. 4. תשדרג את התלויות. 5. תוציא מהחשמל, ספור עד 10 ותחזיר חזרה. לפעמים אין ברירה. אני יודע, לפעמים כולם בלחץ, השרת הפסיק לעבוד, צריך עכשיו שיחזור, אין מה לעשות תזרוק עליו נעליים עד שיתחיל לתפקד שוב. הבעיה עם לזרוק נעליים על השרת היא לא שזה לא עובד (נו טוב, קצת שזה לא עובד), אלא שזה ממכר, ובמיוחד אם איזה נעל טלטלה אותו והוא חזר לעבוד. המטרה שלנו היא להתרגל להיות אלה שעוצרים לחשוב ומוכנים להתאמץ כדי להבין. לזרוק נעליים על השרת רק מרחיק אותנו משם. נ.ב. לזרוק נעליים על השרת = לזרוק פרומפטים ל AI

ToCode
1 418
פיתרון Advent Of Code 2024 יום 4 בשפת Ruby את התרגיל הזה קלוד לא הצליח לפתור, לפחות לא עם הפרומפט הפשוט שהדבקתי לו, לכן כבר הרגשתי שמח כשפתרתי אותו לבד. בסיפור שלנו היום אנחנו מקבלים קלט בצורה של מטריצה עם כל מיני אותיות למשל:
..X...
.SAMX.
.A..A.
XMAS.S
.X....
בחלק הראשון עלינו למצוא את כל המופעים של המילה XMAS בקלט אבל בכל הכיוונים (כולל אלכסונים והפוך), כמו בתפזורת מילים. בחלק השני צריך לחפש רק מופעים של MAS שמגיעים בצורת X כלומר:
M.S
.A.
M.S
וגם הם יכולים להיות הפוכים. התרגיל המקורי נמצא בעמוד שלהם כאן: https://adventofcode.com/2024/day/4 פיתרון חלק 1 בשביל שני החלקים היה לי נוח לשמור את הקלט במילון, כשהמפתח הוא אינדקס והערך הוא התו שנמצא באינדקס הזה. בתרגילים של AoC למדתי שיותר קל לעבוד עם מילונים כאלה מאשר עם מערכים דו מימדיים. זה הקוד שיוצר את המילון ברובי:
$matrix = File.read('input.txt').lines.each_with_index.flat_map do |line, line_index|
  line.split('').each_with_index.map do |char, column_index|
    {[line_index, column_index] => char }
  end
end.reduce({}) {|acc, val| acc.merge(val) }
קצת ארוך אבל עובד. אם יש לכם רעיונות יותר קצרים אפשר לשתף בתגובות. הפונקציה הבאה שכתבתי היתה go שפשוט מתקדמת מספר צעדים בכיוון מסוים. הכיוון הכללי לפיתרון היה לרוץ על כל האינדקסים במילון, מכל אינדקס להתקדם 4 צעדים לכל 8 הכיוונים ולראות אם כתובה שם המילה XMAS. הפונקציה go נראית כך:
def go(start, direction, steps = 4)
  Enumerator.produce(start) { |r, c| [r + direction[0], c + direction[1]] }.take(steps)
end
והפונקציה המרכזית של הפיתרון היא count_words_around שלוקחת נקודה ומריצה את go לכל 8 הכיוונים ואז סופרת כמה מהכיוונים האלה מכילים את המילה XMAS:
def count_words_around(point)
  indexes = [[0, 1],
             [1, 1],
             [1, 0],
             [1, -1],
             [0, -1],
             [-1, -1],
             [-1, 0],
             [-1, 1]].map { |direction| go(point, direction, 4) }
  indexes.count { |path| $matrix.slice(*path).values.join('') == 'XMAS' }
end

pp $matrix.keys.map {|point| count_words_around(point) }.sum
חלק 2 בחלק השני יצרתי מטריצה באותו אופן אבל על הפונקציה go כבר וויתרתי. במקום ללכת 4 צעדים מספיק לקחת את ה X שמסביב לנקודה ולבדוק אם כתוב בו MAS או SAM. זה הקוד:
def count_words_around(point)
  return 0 if $matrix[point] != 'A'

  around = [
    [-1, -1],
    [0, 0],
    [1, 1],
    [-1, 1],
    [0, 0],
    [1, -1]
  ].map { |direction| [point[0] + direction[0], point[1] + direction[1]] }
    .map {|i| $matrix[i] }.join('')

  if %w[MASMAS MASSAM SAMMAS SAMSAM].include?(around)
    1
  else
    0
  end
end

pp $matrix.keys.map {|point| count_words_around(point) }.sum

ToCode
1 418
היום למדתי: הסיפור עם bcrypt ו 72 בתים לא ברור איך העברתי חיים שלמים בלי לשים לב למגבלה הזאת, אבל עכשיו שגיליתי אותה אני משתף כאן כדי שאתם לא תיפלו באותו בור. לכוכב בסיפור של היום קוראים bcrypt והוא אלגוריתם Password Hashing ישן שעדיין נמצא בשימוש נרחב בהרבה מערכות. בין השאר ספריית Devise שהיא הספריה הפופולרית לניהול משתמשים ב Rails משתמשת בו. בגדול bcrypt נחשב עדיין מאובטח למרות גילו המבוגר, אבל יש לו מגבלה מתועדת (ומסתבר שמאוד מפורסמת) - הוא לא יודע לערבל סיסמאות ארוכות יותר מ 72 בתים. מה זה אומר? הנה בקוד ריילס:
> u = User.new(name: 'a', email: 'ynon@gmail.com', password: '0' * 80 + '1')
> u.valid_password?('0' * 80 + '2')
 => true
כלומר בדיקת הסיסמאות היא רק על 72 הבתים הראשונים של הסיסמה. כשיש סיסמה ארוכה מ 72 בתים אנחנו מתעלמים מכל מה שבא אחרי. לקחים? אם אפשר השתמשו ב Aragon2 במקום ב bcrypt, ובכל מקרה כשאתם בוחרים סיסמה לאתר שימו לב לא להתחיל את הסיסמה ב 72 תווים שקל לנחש ורק אז לשים את הסיסמה האמיתית. מי יודע אם הם לא משתמשים בטעות ב bcrypt וחותכים את מה שבא אחרי ה 72. נ.ב. הפוסט הזה בנושא סופר מעניין וסוקר את המימוש של bcrypt בעוד שפות תכנות בדגש על איזה ספריות יסרבו לעבוד עם קלט שאורכו מעל 72 בתים לעומת איזה ספריות פשוט ימחקו את החלק שאחרי 72 כדי שבכל זאת דברים יעבדו: https://n0rdy.foo/posts/20250121/okta-bcrypt-lessons-for-better-apis/

ToCode
1 418
טיפ פייתון: מחיקת שורה ראשונה מטקסט עם StringIO הפונקציה הבאה מקבלת מחרוזת טקסט ומחזירה את הטקסט בלי השורה הראשונה:
def remove_first_line(text):
    return '\n'.join(text.splitlines()[1:])
אבל אם בטעות נעביר ל None היא תזרוק Exception. בנוסף חיבור המחרוזות עם n עלול להיות מבלבל כי מי שיגיע לקרוא את הקוד עלול לתהות מה קורה ב Windows שם תו סוף השורה הוא שונה. לפעמים זה רעיון טוב לעצור את התוכנית אם מקבלים None, אבל לפעמים אנחנו רוצים לבנות משהו יותר גמיש. דרך נוספת למחוק את השורה הראשונה היא הקלאס StringIO מתוך המחלקה io. הנה גירסה שנייה של אותה פונקציה:
def remove_first_line(text: str):
    f = StringIO(text)
    f.readline()
    return f.read()
הפונקציה יוצרת אוביקט שנראה כמו קובץ, אבל בעצם קורא מחרוזת מרובת שורות. בעזרת המתודות readline ו read אנחנו יכולים לקבל את כל השורות שאחרי הראשונה, בלי לפרק ולהדביק מחדש את השורות וגם אם מעבירים לפונקציה None הכל עדיין מסתדר והיא תחזיר מחרוזת ריקה.

ToCode
1 418
לספר מה בנינו או לדעת מה נבנה? מה עדיף, לכתוב את הבדיקות לפני או אחרי הקוד? ואת דף הפרויקט בגיאהב? ואת התיעוד? והמדריך למשתמש? בעבודה על פרויקט צד, רבים מעדיפים קודם לבנות את הפרויקט ואז לספר לעולם מה בנינו: אחרי כתיבת הקוד מנסחים הודעת קומיט, אחרי שהקוד מוכן כותבים בדיקות ותיעוד, אחרי שהפרויקט באוויר כותבים דף פרוקיט בגיטהאב ואחרי שהוצאנו גירסה חדשה כותבים ב Release Notes מה עשינו. התוצאה הרבה פעמים היא פרויקט "שצריך עוד תשומת לב" ומפתח שכבר התעייף מתחזוקה שלו. חלק גדול מהגישה הזאת נובע מפחד, פחד להתחייב. אם נשים את הפחד בצד רגע נוכל לנסות גישה שונה: 1. בפרויקט קוד פתוח - מתחילים מבניית דף גיטהאב לפרויקט ומסבירים מה יהיה בו, מה הארכיטקטורה, מה התהליכים המרכזיים במערכת, באיזה טכנולוגיות נשתמש וכן גם איך אפשר לתרום קוד לפרויקט. כבר בקומיטים הראשונים מייצרים את ה Action שמעביר את הפרויקט לפרודקשן. 2. בפרויקט בתשלום - מתחילים מבניית דף נחיתה לפרויקט שמסביר מה אנחנו רוצים לבנות, כולל "צילומי מסך" ו Use Cases למוצר, והכי חשוב תיבה לאיסוף מיילים של אנשים שמתעניינים בפרויקט. נכון, לבנות מוצר לפי Spec זה פחות מלהיב מ"לגלות תוך כדי תנועה", אבל מגדיל את הסיכויים שהפרויקט יראה אור יום בתור פרויקט שנהיה גאים בו.