Improving my solution to the Quiz project from Gophercises in Golang I'm new to Go and I completed the first challenge from Gophercises. Any feedback would be highly appreciated.
There are some things that I am unsure of if they are made in an optimal way.
Github repo

Goroutines and channels in the quizList loop

Better error logging for the cli portion

The Shuffle function only works for string, how can I make it work for other types as well?

How should I approach writing test cases?

How should the folder structure be structured?
 package main

 import (
     &quot;encoding/csv&quot;
     &quot;flag&quot;
     &quot;fmt&quot;
     &quot;log&quot;
     &quot;math/rand&quot;
     &quot;os&quot;
     &quot;strings&quot;
     &quot;time&quot;
 )

 type question struct {
     prompt, answer string
 }

 func main() {

     var (
         csvFile = flag.String(&quot;csv&quot;, &quot;problems.csv&quot;, &quot;a csv file in the format of 'question,answer'&quot;)
         timeLimit = flag.Int(&quot;limit&quot;, 3, &quot;the time limit for the quiz in seconds&quot;)
         isShuffle = flag.Bool(&quot;shuffle&quot;, false, &quot;shuffle the quiz order each time it is run&quot;)
     )

     flag.Parse()

     if *csvFile == &quot;&quot; {
         fmt.Printf(&quot;%s does not exist, please choose another file&quot;, *csvFile)
     } 

     if *timeLimit == 0 {
         fmt.Printf(&quot;Not a valid limit, please choose another limit&quot;)
     }

     score := 0

     file, err := os.Open(*csvFile)

     if err != nil {
         log.Fatal(err)
     }

     r := csv.NewReader(file)

     problems, err := r.ReadAll()

     if err != nil {
         log.Fatal(err)
     }

     if *isShuffle {
         Shuffle(problems)
     }

     quizList := populateQuiz(problems)

     timer := time.NewTimer(time.Duration(*timeLimit) * time.Second)

     isTimerDone := true

     ch := make(chan string)

     for _, quiz := range quizList {

         fmt.Printf(quiz.prompt)

         var recipientAnswer string

         go func() {
             fmt.Scanln(&amp;recipientAnswer)
             ch &lt;-recipientAnswer
         }()

         if isTimerDone {
             select {
             case &lt;-ch:
                 if recipientAnswer == quiz.answer {
                     score += 1
                 }
             case &lt;-timer.C:
                 isTimerDone = false
                 fmt.Println()
                 return
             }
         } else {
             break
         }

     }

     fmt.Printf(&quot;\nYou scored %d out of %d.\n&quot;, score, len(problems))

 }

 func Shuffle(l [][]string) {
     rand.Seed(time.Now().UnixNano())

     rand.Shuffle(len(l), func(i, j int) {
         l[i], l[j] = l[j], l[i]
     })
 }

 func populateQuiz(problems [][]string) []question {

     var questions []question 

     for index, record := range problems {

         prompt := fmt.Sprintf(&quot;Problem: #%d: %s = &quot;, index + 1, record[0])
         answer := record[1]

         questions = append(questions, question{
             prompt: prompt, 
             answer: strings.TrimSpace(answer),
         })

     }

     return questions
 }
from what I saw there are a few changes that could be done to the program

while parsing command-line arguments I think it's better to put them inside a function cause it makes the code look clean and simple and as it is returned as a pointer you would have to keep getting the value as * but you could return it as the specific type and have a simpler handling
 csvFile, timeLimit, isShuffle := parseCMD() 

 func parseCMD() (string, int, bool) {
 csvFile := flag.String(&quot;csv&quot;, &quot;problems.csv&quot;, &quot;a csv file in the format of 'question,answer'&quot;)
 timeLimit := flag.Int(&quot;limit&quot;, 3, &quot;the time limit for the quiz in seconds&quot;)
 isShuffle := flag.Bool(&quot;shuffle&quot;, false, &quot;shuffle the quiz order each time it is run&quot;)
 flag.Parse()
 return *csvFile, *timeLimit, *isShuffle

}

var (
csvFile = flag.String(&quot;csv&quot;, &quot;problems.csv&quot;, &quot;a csv file in the format of 'question,answer'&quot;)
timeLimit = flag.Int(&quot;limit&quot;, 3, &quot;the time limit for the quiz in seconds&quot;)
isShuffle = flag.Bool(&quot;shuffle&quot;, false, &quot;shuffle the quiz order each time it is run&quot;)
)
var can be used on a variable that doesn't have any initialization or if it's gonna be a global score or... if in a case outside an if statement or a for loop (a situation where:= won't work)..  as these are being declared inside a function and have a default value a short variable declaration would be a more golang kinda way. (there is not a lot of difference between using var and:= in your code but in a situation like this:= would be more golang-ish)
https://stackoverflow.com/questions/53404305/when-to-use-var-or-in-go/53404332
https://stackoverflow.com/questions/21657446/var-vs-in-go




 timer := time.NewTimer(time.Duration(*timeLimit) * time.Second)

while defining a ticker add a &quot;defer ticker.stop()&quot; to release any resource related to as it wont be garbage collected (https://golang.org/src/time/tick.go)

if isTimerDone {
select {
case &lt;-ch:
if recipientAnswer == quiz.answer {
score += 1
}
case &lt;-timer.C:
isTimerDone = false
fmt.Println()
return
}
} else {
break
}


In the above case the if check (isTimerDone) won't actually hit as by flow you always go into the select case and if you get an answer you continue the loop and if the timer hits you return.. so a better way to write it would be to remove the IF-ELSE check..
from what I understand yours is a score counting program so assuming you don't answer and the timer hit.. you could continue the loop and score based on the total answer or break the loop if he doesn't answer (this is just a suggestion :) )