A command-line quiz application for a take home assignment built with Go, featuring a gRPC API server and CLI client. Users can take quizzes, get feedback, and compare their performance against other participants.
gRPCAPI Server- Command-line interface for taking the quiz
- In-Memory Storage
- Performance comparison with other participants
- Backend:
Go API:gRPCwithProtocol Buffers- Storage: In-memory
- CLI Framework:
Cobra - Error Handling: Domain-specific error types with stack traces
- Testing: Unit and integration tests
-
gRPC: I prefergRPCoverRESTfor a couple reasons:- Development experience
- Schema first approach. The
.protofile are the source of truth. In a sense, similar toGraphQL. - Language agnostic
- Performance
- Growing ecosystem
- Used by
Kubernetes,Etcd,Cockroach DB, etc.
-
Design:
- The
APIor server layer (pkg/serverandpkg/api[proto]) processes the requests data to and from the service layer. - Service layer (
pkg/qservice) has te business logic and queries thestore. - Store layer interacts with the in-memory database
run.gostarts off theserver.- Minimal
mainfunctions.
- The
-
Testing:
- Tests per package
- Integration test
- E2E test
-
Errors:
-
For the error design I opted to follow the opinionated approach from the book Concurrency in Go by Katherine Cox-Buday.
-
If an error is a known edge case, you return the error wrapped on a custom error type, if not, you return the error as is. E.g:
solutions, err := qs.store.Solutions() if err != nil { if _, ok := err.(store.StoreError); !ok { // Bug return nil, err } // Known edge case. Wrap it and return. return nil, ServiceError{qerr.Wrap(err, qerr.Internal, "failed to get solutions")} }
-
-
CLI:
Cobraas specifiedpromptuifor interactivity
Go- Optional:
Protocol Bufferscompiler (protoc) andMake.
Clone the repository:
git clone https://github.com/mateopresacastro/qstnnr.git && cd qstnnrBuild the application:
make buildThis will create two binaries in the bin directory:
server: The gRPC server
qstnnr: The CLI client
To run the quiz run qstnnr server start and then qstnnr take:
➜ bin/qstnnr server start
Server started on port 4000 (PID: 12152)➜ bin/qstnnr take
Question 1 of 10
Use the arrow keys to navigate: ↓ ↑ → ←
What is the purpose of the blank identifier (_) in Go?
➜ To discard an unwanted value
To declare a private variable
To create an anonymous function
To mark a variable as nullableThe CLI has two main commands: server and take.
➜ bin/qstnnr help
A CLI application to check you Go knowledge.
Usage:
qstnnr [command]
Available Commands:
completion Generate the autocompletion script for the specified shell
help Help about any command
server Manage the qstnnr server
take Take the quiz
Flags:
-h, --help help for qstnnr
Use "qstnnr [command] --help" for more information about a command.To see the help for the server command run:
➜ bin/qstnnr server help
Commands to start, stop, restart and check status of the qstnnr server
Usage:
qstnnr server [command]
Available Commands:
restart Restart the qstnnr server
start Start the qstnnr server
status Check the status of the qstnnr server
stop Stop the qstnnr server
Flags:
-h, --help help for server
Use "qstnnr server [command] --help" for more information about a command.You can set PORT in your env vars before starting the server. By default it uses 5974.
➜ bin/qstnnr server start
Server started on port 5974 (PID: 95853)➜ export PORT=4000
➜ bin/qstnnr server start
Server started on port 4000 (PID: 96592)The take command starts the quiz. At the end you can see your results.
➜ bin/qstnnr take
Question 1 of 10
Use the arrow keys to navigate: ↓ ↑
What is the zero value for a pointer in Go?
➜ 0
undefined
void
nil├── cmd/ # Application entrypoints
│ ├── cli/ # CLI implementation
│ └── server/ # Server implementation
├── pkg/
│ ├── api/ # gRPC protocol definitions
│ ├── qerr/ # Error handling
│ ├── qservice/ # Business logic
│ ├── server/ # gRPC server implementation
│ └── store/ # Data storage
├── Makefile # Build and development commands
├── questions.go # Quiz content and initial data
└── run.go # Main application setup and server initializationmake start-server # Start the servermake start-cli # Start the cli with the take commandmake build # Build all binariesmake build-server # Build server onlymake build-cli # Build CLI onlymake test # Run all tests with coverage and race detectionmake proto # Generate Go code from .proto files