HiTakeJobHiTakeJob

שאלות ראיון Go (Golang) — 34 שאלות ותשובות

זמן קריאה: 8 דקות

שאלות ראיון Go מתרכזות בקונקרנטיות (goroutines ו-channels), במודל ה-interfaces הייחודי, בניהול שגיאות מפורש, ובניהול זיכרון. במדריך זה ריכזנו 34 שאלות ותשובות אמיתיות, מקובצות לפי נושא, כפי שנשאלות בראיונות בישראל. נכון להיום פתוחות אצלנו 56 משרות Go — מהתחומים הצומחים ביותר בתשתיות ובקלאוד.

יסודות השפה

למה נבנתה Go ומה מייחד אותה?

Go נבנתה לפשטות, קומפילציה מהירה, וקונקרנטיות מובנית. היא מקומפלת לבינארי סטטי יחיד, בעלת garbage collector, ותחביר מינימלי בכוונה — מה שהופך אותה נפוצה בשירותי רשת, DevOps וקלאוד.

מה ההבדל בין slice ל-array?

array הוא בעל גודל קבוע וחלק מהטיפוס. slice הוא מבנה דינמי המצביע למערך בסיס, עם length ו-capacity. כמעט תמיד עובדים עם slices; מבינים ש-append עשוי להקצות מערך חדש כאשר עוברים את ה-capacity.

איך פועל map ב-Go?

map הוא טבלת גיבוב מובנית. הוא אינו בטוח ל-goroutines מקבילות ללא סנכרון, וסדר האיטרציה עליו אקראי בכוונה. לגישה מקבילה בטוחה משתמשים ב-sync.Mutex או ב-sync.Map.

מה ההבדל בין value receiver ל-pointer receiver?

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

מה קורה עם zero values ב-Go?

כל טיפוס בעל ערך אפס: 0 למספרים, "" למחרוזות, nil ל-pointers/slices/maps. אין ערכים לא-מאותחלים. תכנון טוב מנצל את ה-zero value כמצב שימושי (למשל struct שמוכן לשימוש מיד).

מה ההבדל בין new ל-make?

new מקצה זיכרון ומחזיר pointer ל-zero value של הטיפוס. make משמש רק ל-slices, maps ו-channels — הוא מאתחל את המבנה הפנימי ומחזיר ערך מוכן לשימוש, לא pointer.

מה ההבדל בין make([]int, 0) ל-var s []int?

שניהם slices תקינים ש-append עובד עליהם. var s []int הוא nil slice (len=0, cap=0), ו-make([]int, 0) הוא non-nil ריק. ההבדל מהותי בעיקר בהשוואה ל-nil ובסריאליזציה ל-JSON (nil הופך ל-null, ריק ל-[]).

Interfaces וטיפוסים

איך עובדות interfaces ב-Go?

interface מוגדר על ידי קבוצת מתודות, וטיפוס מממש אותו implicitly — ללא הכרזה מפורשת (structural typing). זה מאפשר צימוד רופף: מגדירים interface קטן במקום הצריכה, לא במקום המימוש.

מה זה empty interface (any)?

interface{} (או any מ-Go 1.18) יכול להחזיק כל ערך. שימושי לגנריות ישנה, אך מאבד בטיחות טיפוסים ומחייב type assertion. עדיף להימנע ולהעדיף generics או interfaces ממוקדים.

מה זה type assertion ו-type switch?

type assertion (v, ok := x.(T)) בודק ומוציא טיפוס קונקרטי מ-interface. type switch מטפל בכמה טיפוסים אפשריים בבלוק אחד. שימוש ב-ok מונע panic כאשר הטיפוס אינו תואם.

מה הוסיפו generics ל-Go?

מ-Go 1.18 אפשר לכתוב פונקציות וטיפוסים עם פרמטרי טיפוס ואילוצים (constraints). זה מאפשר קוד גנרי בטוח-טיפוסים (למשל Map/Filter) בלי interface{} ובלי שכפול קוד.

קונקרנטיות

קונקרנטיות היא לב הראיון ב-Go. הטבלה מסכמת את הכלים המרכזיים:

כליתפקידמתי
goroutineיחידת ריצה קלהעבודה מקבילה
channelתקשורת וסנכרוןהעברת נתונים בין goroutines
selectהמתנה על כמה channelstimeout, מיזוג אירועים
contextביטול ותפוגהעצירת עבודה בבקשות
WaitGroupהמתנה לסיום קבוצהfan-out/fan-in

מה זו goroutine ובמה היא שונה מ-thread?

goroutine היא יחידת ריצה קלת-משקל שמנוהלת על ידי ה-runtime של Go, לא על ידי מערכת ההפעלה. היא מתחילה בערך ב-2KB stack וגדלה לפי צורך, כך שאפשר להריץ אלפי goroutines במחיר של מעט threads.

מה זה channel ולמה משתמשים בו?

channel הוא צינור מוקלד לתקשורת ולסנכרון בין goroutines. הפילוסופיה: "Don't communicate by sharing memory; share memory by communicating". channel מונע race conditions בכך שהעברת בעלות עוברת דרכו.

מה ההבדל בין buffered ל-unbuffered channel?

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

מה עושה select?

select ממתין על כמה פעולות channel בו-זמנית ומבצע את זו שמוכנה. משתמשים בו ל-timeout (עם time.After), ל-cancellation, ולמיזוג של כמה מקורות אירועים לתוך goroutine אחת.

מה זה context.Context ולמה הוא חשוב?

Context נושא deadline, ביטול (cancellation) וערכי בקשה לאורך שרשרת קריאות. הוא מאפשר לעצור עבודה של goroutines כאשר בקשה מבוטלת או פג זמנה — קריטי בשירותי רשת למניעת דליפת goroutines.

מה ההבדל בין sync.Mutex ל-sync.RWMutex?

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

איך מזהים ומונעים race condition?

מריצים עם הדגל ‎-race‎ שמזהה גישה מקבילה לא-מסונכרנת. מונעים על ידי channels, sync.Mutex, או פעולות אטומיות מ-sync/atomic. גישה משותפת למשתנה ללא הגנה היא באג גם אם "עובד רוב הזמן".

מה זה sync.WaitGroup?

WaitGroup ממתין לסיום קבוצת goroutines. מוסיפים Add לפני ההרצה, קוראים Done בסיום כל goroutine (עם defer), ו-Wait חוסם עד שכולן סיימו. טעות נפוצה: לקרוא Add בתוך ה-goroutine.

שגיאות, זיכרון וכלים

איך Go מטפלת בשגיאות?

Go מחזירה error כערך מפורש (לרוב הערך האחרון), ובודקים אותו עם if err != nil. אין exceptions לזרימה רגילה. הגישה מפורשת ומקומית, ומעודדת טיפול בשגיאה במקום שבו היא נוצרת.

מה ההבדל בין panic ל-error?

error הוא זרימה צפויה שמטפלים בה. panic מיועד למצבים חריגים ובלתי-הפיכים (באג תכנות). ברוב הקוד לא משתמשים ב-panic; recover נועד רק להחזיר שירות לפעולה בגבול (למשל handler של שרת).

מה זה error wrapping (%w)?

מ-Go 1.13 אפשר לעטוף שגיאה עם fmt.Errorf("...: %w", err), ולבדוק בהמשך עם errors.Is ו-errors.As. זה שומר על שרשרת ההקשר של השגיאה בלי לאבד את הטיפוס המקורי.

מה עושה defer?

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

מה זה sentinel error מול custom error type?

sentinel error הוא ערך שגיאה מוגדר מראש (למשל io.EOF) שמשווים אליו עם errors.Is. custom error type הוא struct שמממש את interface ה-error ונושא הקשר נוסף, ומזהים אותו עם errors.As. הבחירה תלויה בכמות המידע שצריך להעביר עם השגיאה.

איך פועל ה-garbage collector של Go?

Go משתמש ב-GC מקבילי מסוג mark-and-sweep עם השהיות קצרות מאוד (sub-millisecond). הוא מתוכנן ל-latency נמוך, מה שמתאים לשירותי רשת. עדיין כדאי למזער הקצאות בנתיבים חמים.

איך מודדים ביצועים ב-Go?

משתמשים ב-benchmark של חבילת testing ובכלי pprof לפרופיילינג CPU וזיכרון. מזהים allocations מיותרות ו-hotspots. escape analysis קובע אם משתנה עובר ל-heap.

מעשי ומערכתי

איך בונים HTTP server ב-Go?

עם חבילת net/http המובנית: מגדירים handlers, משתמשים ב-ServeMux לניתוב, ומריצים ListenAndServe. Go מספקת שרת production-grade בלי framework חיצוני, אם כי משתמשים לעיתים ב-chi או gin.

מה זה worker pool ולמה משתמשים בו?

Worker pool מגביל את מספר ה-goroutines שמעבדות עבודה בו-זמנית, דרך channel של משימות ו-N workers. זה מונע יצירת אינסוף goroutines ושליטה על עומס משאבים חיצוניים (DB, API).

איך מארגנים פרויקט Go?

עם Go modules (go.mod), חבילות לפי אחריות, cmd/ ל-entry points, internal/ לקוד פרטי. עדיפות לחבילות שטוחות וברורות על פני היררכיה עמוקה. מבנה מוגזם הוא ריח קוד ב-Go.

מתי לבחור Go על פני שפה אחרת?

Go מצוינת לשירותי רשת קונקרנטיים, כלי CLI ותשתיות (Docker ו-Kubernetes כתובים בה). פחות מתאימה לחישוב מדעי כבד או ל-UI. הבחירה נובעת מקונקרנטיות פשוטה ופריסה קלה.

מה ההבדל בין mutex ל-channel לסנכרון?

channel מתאים להעברת בעלות על נתונים ולתיאום זרימה בין goroutines ("share by communicating"). mutex מתאים להגנה על מצב משותף פשוט (מונה, cache) שבו channel יהיה מסורבל. הכלל: channel לתיאום, mutex למצב.

מה זה nil interface מול nil pointer?

interface הוא nil רק אם גם הטיפוס וגם הערך שלו nil. אם מחזירים pointer שהוא nil אך עטוף ב-interface, ההשוואה ל-nil תחזיר false — מקור באג עדין ונפוץ שמראיינים אוהבים לבדוק.

מה זה embedding ב-Go?

embedding מאפשר להטמיע struct או interface בתוך אחר, וכך "לרשת" את השדות והמתודות שלו (composition על פני inheritance). זו הדרך של Go לשימוש חוזר בקוד — הרכבה, לא ירושה קלאסית.

מתי לבחור במה — טבלת החלטות ל-Go

שיקול דעת נכון חשוב בראיון Go לא פחות מהתחביר:

הצורךבחירה מומלצתהסיבה
תיאום זרימה בין goroutineschannelהעברת בעלות בטוחה
הגנה על מצב משותף פשוטsync.Mutexפשוט ויעיל
ביטול ותפוגה של עבודהcontext.Contextמונע goroutine leaks
הגבלת מקביליותworker poolשליטה בעומס משאבים
קוד גנרי בטוח-טיפוסgenericsמחליף interface{}

טעויות נפוצות בראיון Go

  • לשכוח לבדוק err: התעלמות מ-error מסגירה חוסר הבנה של הפילוסופיה.
  • goroutine leak: להריץ goroutine בלי דרך לעצור אותה (בלי context/channel).
  • לתפוס משתנה loop ב-goroutine: באג קלאסי לפני Go 1.22 — לוכדים את אותו משתנה.
  • שימוש יתר ב-interface{}: לוותר על בטיחות טיפוסים במקום generics.
  • לחשוב ש-map בטוח ל-concurrency: גישה מקבילה ל-map גורמת ל-panic.

מיני-צ'קליסט להכנה לראיון Go

  • לתרגל goroutines, channels, select ו-context.
  • להבין slices, maps ו-zero values לעומק.
  • לדעת להסביר interfaces implicit ו-generics.
  • לתרגל טיפול שגיאות, error wrapping ו-defer.
  • להריץ קוד עם ‎-race‎ ולהכיר sync.Mutex/WaitGroup.
  • להכין דוגמת worker pool מקצה לקצה.

לרוחב תפקידי ה-backend ראו את שאלות ראיון Backend בישראל, ולמסלול הכניסה ראו איך הופכים למפתח Backend. מוכנים? עיינו במשרות Go ובכלל המשרות הפתוחות.

שאלות נפוצות

כמה משרות Go יש בישראל כרגע?

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

על מה הכי שמים דגש בראיון Go?

על קונקרנטיות: goroutines, channels, select ו-context, כולל מניעת race conditions ו-goroutine leaks. בנוסף, על מודל ה-interfaces, ניהול שגיאות מפורש, ומבני slice/map. הבנת trade-offs חשובה מאוד.

האם Go קלה ללמידה למי שמגיע מ-Java או Python?

התחביר של Go מינימלי ונלמד מהר, אך מודל הקונקרנטיות (channels, goroutines) וניהול השגיאות המפורש שונים. מי שמבין את הפילוסופיה של "share by communicating" מסתגל מהר; שינון תחביר לבדו לא מספיק לראיון.

האם צריך להכיר framework מסוים ל-Go?

הספרייה הסטנדרטית (net/http) חזקה מספיק לרוב הצרכים, וזו נקודת מבחן. היכרות עם gin או chi יכולה לעזור, אך מראיינים מעריכים יותר הבנה של הבסיס מאשר תלות ב-framework.

מה ההבדל בין ראיון Go לג'וניור לסניור?

לג'וניור מתמקדים בתחביר, slices/maps וקונקרנטיות בסיסית. לסניור מצפים לתכנון worker pools, ניהול context ו-cancellation נכון, פרופיילינג ביצועים, והחלטות ארכיטקטורה בשירותים מבוזרים.

מאמרים נוספים

המשרות באתר