• בלוג
  • ואיך את יודעת מה המערכת עושה?

ואיך את יודעת מה המערכת עושה?

31/10/2026

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

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

class IssueInvitation
  class Rejected < StandardError; end

  def self.call(email:, admin:)
    raise Rejected, I18n.t("admin.forbidden") unless admin.admin?

    token = SecureRandom.urlsafe_base64(32)
    invitation = nil
    # Serialize issue/resend calls, including invitations without an existing row.
    admin.with_lock do
      invitation = Invitation.find_or_initialize_by(email: email.to_s.strip.downcase)
      raise Rejected, I18n.t("admin.invitations.existing_user") if User.exists?(email: invitation.email)
      if invitation.accepted_at?
        raise Rejected, I18n.t("admin.invitations.already_accepted")
      end
      if invitation.last_sent_at && invitation.last_sent_at > 1.minute.ago
        raise Rejected, I18n.t("admin.invitations.cooldown")
      end
      invitation.assign_attributes(invited_by: admin, token_digest: Invitation.digest(token),
        expires_at: 7.days.from_now, last_sent_at: Time.current)
      invitation.save!
    end
    InvitationDeliveryJob.perform_later(invitation.id, token)
    invitation
  end
end

הסוכן יצר מנגנון קירור כדי לוודא שאני לא שולח יותר מדי הזמנות, אלה השורות:

if invitation.last_sent_at && invitation.last_sent_at > 1.minute.ago
  raise Rejected, I18n.t("admin.invitations.cooldown")
end

בנוסף אני רואה שכל שליחת הזמנה מעדכנת token של ההזמנה כך שאם אני שולח מספר הזמנות לאותה כתובת הנמען יכול להשתמש רק בהזמנה האחרונה. אלה השורות:

token = SecureRandom.urlsafe_base64(32)
...
invitation.assign_attributes(invited_by: admin, token_digest: Invitation.digest(token),
        expires_at: 7.days.from_now, last_sent_at: Time.current)

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

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