Metazi
Status
📜📞🔧❌
Back-End Developer
Interview Process
flowchart LR
    sr(Send Resume) --> hr(HR Call) --> ti(Technical Interview) --rejected--x hri(HR Interview) -.-> o(Offer)
Apply Way
Jobvision
Interview Date
- 
Sent Resume
1404/07/01 - 
HR Call
1404/07/06 - 
Technical Interview
1404/07/07 - 
Rejection Email
1404/07/08 
Interview Duration
- Technical Interview
45 minutes 
Interview Platform
Google Meet
Technical Interview
- 
Tell me about yourself.
 - 
What is
initin Go? How does it work and how does it differ frommain?Answer
init()is a special function:func init(). It runs automatically after package-level variables are initialized. You can have multipleinit()s in a package. You cannot callinit()yourself.Order: evaluate package vars → run that package’s
init()s → repeat by dependency order → finally callmain.main().main()is the program entry point (package main,func main()) and runs once after allinit()s finish. - 
What is
_in imported packages?Answer
_is the blank identifier. When used in an import likeimport _ "pkg/path"it means: import the package solely for its side effects (package initialization) but do not bind any name to refer to it in the importing file. The compiler won’t complain about an unused import because the blank identifier intentionally discards the package name.import ( _ "github.com/lib/pq" // register pq as a database/sql driver via its init() )(Aside:
_is also used to discard values in assignments:_, ok := m["x"].) - 
Why use it? If there is no need, should we remove it? Why do people still use it?
Answer
Use them when the package’s
init()performs necessary side effects (driver registration, plugin/handler registration, global initialization). If a package has no needed side effects, remove the import. - 
Why write configs in
initinstead ofmain? What are the benefits? What are the problems if we write all code ininitinstead ofmain?Answer
Pros:
init()runs beforemain(), so it can prepare package-level state.Cons:
init()cannot acceptcontextor return errors, runs during tests, runs before flag/config parsing, and makes shutdown/signal handling hard. Prefermain()for orchestration, lifecycle and graceful shutdown. - 
Multiple packages each run an
initthatPrintfs the package name. You import them inmain, butmain()is empty. What is the output?Answer
// pkg/a/a.go package a import "fmt" func init() { fmt.Println("init a") } // pkg/b/b.go package b import "fmt" func init() { fmt.Println("init b") } // main.go package main import ( _ "example.com/project/pkg/a" _ "example.com/project/pkg/b" ) func main() {} // emptyImportant notes about the order:
- Initialization (and therefore the 
init()prints) runs beforemain()is called. - The order between packages follows the import dependency graph: if 
bimportsa,init awill always appear beforeinit b. - If the packages are independent (neither imports the other), the relative order is unspecified — you should not rely on a particular ordering.
 main()being empty produces no additional output.
 - Initialization (and therefore the 
 - 
If you have multiple goroutines that fetch data from different APIs and you want to gather all results and aggregate them, or wait with a timeout then aggregate, how can you do this?
Answer
package main import ( "fmt" "sync" "time" ) type Result struct { URL string Body string Err error } func fetch(url string) (string, error) { // do actual HTTP call... return "data-for-" + url, nil } func fetchPartial(urls []string, timeout time.Duration) map[string]string { var wg sync.WaitGroup resultsCh := make(chan Result, len(urls)) for _, u := range urls { wg.Add(1) go func(u string) { defer wg.Done() body, err := fetch(u) resultsCh <- Result{URL: u, Body: body, Err: err} }(u) } // close channel when all goroutines are done go func() { wg.Wait() close(resultsCh) }() aggregated := make(map[string]string) timeoutCh := time.After(timeout) Loop: for { select { case r, ok := <-resultsCh: if !ok { break Loop // channel closed, all responses received } if r.Err == nil { aggregated[r.URL] = r.Body } else { // optionally log r.Err } case <-timeoutCh: // timeout occurred — stop waiting for more, but goroutines may still be running. // To avoid leaks in real code, pass a context to requests so they cancel. break Loop } } return aggregated } func main() { urls := []string{"a", "b", "c"} out := fetchPartial(urls, 2*time.Second) fmt.Println(out) } - 
If you run
main.go, how many goroutines are initialized?Answer
At least one main goroutine plus several runtime goroutines (GC, timers, netpoller). You commonly see 2–4 at startup, but exact number varies.
GOMAXPROCScontrols parallelism (Ps), not total goroutine count. - 
How does Go manage goroutines?
Answer
The runtime scheduler uses three core concepts: G, M, and P.
- G (goroutine): the user-level unit of concurrency (stack, state, metadata). Goroutines are cheap to create and their stacks grow and shrink as needed.
 - M (machine): an OS thread that actually executes Go code. The runtime creates and reclaims Ms as needed (e.g., to service blocking syscalls).
 - P (processor): a scheduler context that holds a run queue of runnable Gs and scheduling state. Only an M that has an associated P can run Go code. 
GOMAXPROCScontrols how many Ps exist (how many goroutines can run in parallel). 
How scheduling works (high level):
- Each P has a local run queue of runnable goroutines. When a goroutine becomes runnable it’s pushed to a P’s queue. When a P runs out of work it will steal goroutines from other Ps or pull from a global queue; this provides locality and load balancing.
 - The scheduler chooses a G from a P’s run queue and runs it on the M bound to that P. If that G blocks (channel wait, sleep, blocking syscall), it is parked and another G is scheduled.