The tech stack at Pace.

Mat Ryer · David Hernandez · 26 Feb 2020

The tech stack at Pace.

Mat Ryer · David Hernandez · 26 Feb 2020

Our tech stack was chosen for its simplicity and ease of maintenance, as well as how familiar and comfortable we were with the options.

We also strongly value technologies that we enjoy using. This isn’t just privilege or idealistic thinking, there is an obvious link between happiness and productivity.

Serverless is a great option for new projects

For us, the easiest choice was using Google App Engine, “the original Serverless platform”. Serverless as a buzzword is very popular these days, but it also has its critics. For example, it doesn’t get much love from @dhh:

–@DHH talking about the serverless virtuosity, however he is considering the Serverless functions, where complex systems are deployed as little functions.

In our opinion, Serverless is about pricing (pay per use) and operability (Platform As A Service).

Pace is a software as a service offering, so it has hosted business logic behind APIs, data and file storage, internal pubsub, fronted by a rich client running in the browser. A lot of static assets, and some Go code that needs to be available to run when customers start using the app, and the app starts using our APIs.

We want to focus on the user experience of our application, and not have to worry about how to operate the servers. Components built properly for Google App Engine, and other similar platforms, can scale from zero to the absurd very quickly, helped in part by how quickly it compiles and runs.

Getting operability right takes an enormous amount of time. Things like provisioning, monitoring, backup, scalability, are managed for us, and we can focus just on the end to end functionality.

Google App Engine as the Serverless choice

Our little monolith written in Go, and the static assets, can be deployed to Google App Engine via the gcloud app deploy command, and it will spin up enough instances to meet demand within configured boundaries. They charge on a pay-as-you-use model so you will only pay once things start getting used.

This makes Google App Engine perfect for new projects.

Go has been an option on Google App Engine for some years, and its support keeps improving and today is considered to be of the highest standard.

Google App Engine is a stable and solid choice for building new apps, from small to global scale

By extension Google Cloud Run is also a good choice, if you need more control over the runtime environment.

Front end: Starting small

We knew we needed a sophisticated front-end solution, especially if we wanted to deliver a high quality user experience.

The industry is focusing on using component driven frameworks like React and Vue. Our first front-end prototype was written in Vue.

But after a week or two it didn’t feel right. We were struggling with the components, fighting with the state management and routing, and we were being driven crazy by the number of dependencies that we had at runtime, for such a little app at that time.

Svelte saved us from the front-end framework madness

It was more or less clear that we didn’t enjoy doing Vue components and in a moment of frustration, we tried svelte.dev. It was a joy to set up and getting started.

Svelte appealed because it does all of its work at compile time.

Most frameworks run in the browser and play an active role in the running of your front-end application. With Svelte, everything is wired-up when you build your code. This means the output JavaScript file is smaller, you only have one dependency at runtime, and there’s also much less indirection in the code, so things feel noticeably faster.

You still build components in a similar way to other front-end frameworks, and manage events and properties as you might expect, so any experience with other frameworks is immediately transferrable for teams considering a change in this direction.

But even with similar syntax and components model, we felt that keeping the code clean was much easier, and the feedback loop making changes and refactoring was better. We definitely started to enjoy doing components, and now we are big fans of the framework.

We were able to build useful little components like the Grouper component for Svelte that we wrote.

Svelte from the beginning was a joy to install and get started check out the interactive tutorial on the website.

Backend services: Go

Go is a small and relatively new (ten years old) 100% open-source language developed at Google, and has proven itself as a great language for systems and web services, as well as all sorts of other things.

We are using Go for all of our business logic. The services are accessed via APIs from the browser via a home grown lightweight API layer described in the next section.

Services: Should I use REST or RPC?

It seems that whenever you do an API, the same conversation repeats, should I use REST or RPC or what about GraphQL?

We like the simplicity of REST specifically because it is so easy to use and debug in the browser, but like the good Go programmers we are, we like strongly typed interfaces, and REST lacks it.

On the RPC side, we have tried gRPC and protobuf, but there is a learning curve with the framework, from the protobuf syntax, to making it work on the browser, and once there is a problem it became more complex to debug. We wanted to generate our own code from the definition files, but that involved consuming protobuf messages and all we wanted to do was to send some text to the browser.

Often widely used tools and frameworks grow more complex because of all the different situations they are used in. This is expected but means there’s can be more effort to put in up-front, and more complexity overhead in your codebase.

Solving just our problems

We can keep the simplicity and browser friendless of REST, with the strong interfaces of RPC if we opt to solve only our own problems in a home grown code generation RPC tool.

Services in our backend code are defined in an RPC interface style in Go.

For example, a service welcoming somebody might look like this:

type WelcomeService interface {
	// Greet greets somebody.
	Greet(GreetRequest) GreetResponse
}

type GreetRequest struct {
	Name string
}

type GreetResponse struct {
	Greeting string
}

We then generate client and server code that provides the glue for the service methods.

We implement the service in the server code in Go:

func (welcomeService) Greet(ctx context.Context, r GreetRequest) (*GreetResponse, error) {
	resp := GreetResponse{
		Greeting: "Hello " + r.Name,
	}
	return resp, nil
}
  • The signature of the Greet method is defined by generated code which also produces HTTP wrappers for each method, making that invisible once the tooling is in place
  • Since this is just normal Go code, we have unit tests that call these methods directly dealing with the same GreetRequest and GreetResponse types that the front-end code does

And call it in the front-end:

import { WelcomeService } from '/services.gen.js'

const welcomeService = new WelcomeService()
welcomeService.greet({ name: 'Mat' })
	.then((response) => alert(response.greeting))
	.catch((err)     => alert('Oops, something went wrong: ' + err))

The WelcomeService and even the greet method are all generated from the original definition file.

Client-side data validation

Additionally, the JSON objects passed into the service methods are checked for correctness.

Since we are not (yet) using TypeScript, JavaScript would happily allow us to pass any kind of data to the service. As programmers, we would prefer any mistakes to be reported as errors, and so we made our generated client code check the data fields; any mistakes will become errors.

We switch it off in production because by then we trust ourselves to have it right, and who doesn’t love shaving off milliseconds here and there in production code?

This RPC sounds cool, can I use it?

Yes, you can. It’s completely open source and is called Oto.

Learn more about our RPC and http friendly framework at github.com/pacedotdev/oto


Learn more about what we're doing at Pace.

A lot of our blog posts come out of the technical work behind a project we're working on called Pace.

We were frustrated by communication and project management tools that interrupt your flow and overly complicated workflows turn simple tasks, hard. So we decided to build Pace.

Pace is a new minimalist project management tool for tech teams. We promote asynchronous communication by default, while allowing for those times when you really need to chat.

We shift the way work is assigned by allowing only self-assignment, creating a more empowered team and protecting the attention and focus of devs.

We're currently live and would love you to try it and share your opinions on what project management tools should and shouldn't do.

What next? Start your 14 day free trial to see if Pace is right for your team


First published on 26 Feb 2020 by Mat Ryer David Hernandez
#Tech #Golang #Svelte

or you can share the URL directly:

https://pace.dev/blog/2020/02/26/tech-stack-at-pace.html

Thank you, we don't do ads so we rely on you to spread the word.

https://pace.dev/blog/2020/02/26/tech-stack-at-pace.html


You might also like:

Context-aware io.Reader for Go #Tech #Golang

Batching operations in Go #Golang #Patterns

How I write HTTP services after eight years #Golang #HTTP #WebServices

Subscribe:
Atom RSS JSON