Golang / GoLang System Architecture and Testing Interview Questions
How do you design a consistent error model across multiple Go microservices?
In a system with 10+ services, inconsistent error formats force every client to implement different error parsing. A shared error contract — carried in gRPC status details or HTTP Problem Details — enables uniform client-side handling.
// Shared proto for rich error details (google.rpc.Status) // Add error_details.proto to your project import ( spb "google.golang.org/genproto/googleapis/rpc/status" "google.golang.org/grpc/status" errdetails "google.golang.org/genproto/googleapis/rpc/errdetails" ) // Return rich error from gRPC handler func (s *userServiceServer) CreateUser( ctx context.Context, req *pb.CreateUserRequest, ) (*pb.User, error) { if req.Email == "" { // Rich validation error with field-level detail st := status.New(codes.InvalidArgument, "validation failed") detail := &errdetails.BadRequest{ FieldViolations: []*errdetails.BadRequest_FieldViolation{ {Field: "email", Description: "email is required"}, }, } st, _ = st.WithDetails(detail) return nil, st.Err() } user, err := s.repo.Save(ctx, &User{Email: req.Email}) if err != nil { if errors.Is(err, ErrDuplicate) { st := status.New(codes.AlreadyExists, "email already registered") info := &errdetails.ErrorInfo{ Reason: "EMAIL_ALREADY_EXISTS", Domain: "user.service", } st, _ = st.WithDetails(info) return nil, st.Err() } return nil, status.Errorf(codes.Internal, "internal error") } return toProto(user), nil } // Client: extract rich error details _, err := client.CreateUser(ctx, req) if err != nil { st := status.Convert(err) for _, detail := range st.Details() { switch d := detail.(type) { case *errdetails.BadRequest: for _, v := range d.FieldViolations { log.Printf("field %s: %s", v.Field, v.Description) } } } }
More Related questions...