Jobs Console is our new product built to help developers find the best jobs. Developers sign up, specify their preferences such as preferred tech stack, location, and salary range, and then receive the best job matches via email. If they’re interested in a job, flagging their interest will send their profile to the hiring manager who can then opt-in to an email introduction if they’d like to speak to the developer.
Running the Console devtools newsletter means we’re very familiar with all the options available when building a new software product. Building Jobs Console has allowed us to take advantage of that experience to pick the best tools for our own tech stack.
However, we wanted to keep things simple. We’re not building a deep technology product at the cutting edge of science. We want to use modern tools, but Jobs Console is a traditional web application that just consists of a web frontend, a backend API, and a database. We have deliberately chosen boring technology and spent very few innovation tokens.
This is a rundown of the tools and technologies that make up the tech stack.
The Jobs Console backend is an HTTP REST API implemented in Go. It uses the Encore framework to deal with all of the boilerplate functionality such as endpoint generation, documentation, logging, and tracing. Different components interact with each other like microservices, but without all of the complexity - as a developer, it’s just like calling functions from different Go modules. This provides the benefits of separating functionality into individual units without the cognitive overhead of understanding many services with different interfaces.
Encore handles all the platform functionality as well, which includes provisioning a local development environment, tests, staging/preview environments, deploys, and managing cloud infrastructure. It’s a management layer on top of our own Google Cloud Platform account where the code itself is deployed inside a Google Cloud Run container. Postgres is the database (Encore also handles database schema migrations) and user authentication is handled by Firebase Auth.
Encore auto-generates a TypeScript API client which we use as part of our Remix frontend app (all TypeScript). Most of the logic is in the backend, so this is mainly calling those APIs. The UI is implemented using React components where possible and hosted on Vercel. The build times are incredible and Vercel deals with the underlying infrastructure, scaling everything and deploying globally to maximize performance.
The choice of Encore and Remix is where we spent our innovation tokens. Both are relatively new, which is where the risk lies. However, we felt that their advantages outweighed that disadvantage.
In the case of Encore, it solved a lot of platform related problems i.e. we didn’t need to deal with logging, tracing, dev environments, builds, deploys, or provisioning infrastructure with basic features like load balancing. Since we were going to use Go and build a web service inside a container anyway, using Encore made sense.
We picked Remix out of the millions of other JS/TS frameworks because of how lightweight and close to common web standards it is. We almost chose NextJS, which would’ve made integration into certain systems (such as authentication and server side rendering) easier, but picked Remix because of the minimalism.
There’s always something new and shiny to pick from, but given how lightly they tie into our business logic, we can replace them if needed. Hopefully we picked correctly and that won’t be necessary!
In addition to the tools we already use to run Console, there are a few new ones specific to Jobs Console:
- Checkly runs synthetic monitoring to ensure key actions are responding properly. This also runs on new Vercel deployments before they are promoted to live, ensuring no regressions reach production. The backend is monitored with metrics through Google’s Operations Suite.
- Courier manages all our notifications, which are currently just email. It makes looping through content blocks and defining template variables easy. The sending of the emails is handled through SendGrid.
- Doppler manages most of our secrets. In development, the CLI wraps around the Encore backend server and Remix frontend server to inject the appropriate environment variables. It also syncs with Vercel to set the right values in staging and production. Encore has its own secrets management (a layer on top of Google’s Secret Manager) for the backend.
- GitHub is where all our code is. Merging into main triggers Encore to deploy the backend and Vercel to deploy the frontend. The test suite is run and if everything passes, the new version is put live. Commit to live takes less than 90 seconds (including the test runs). Dependabot keeps all our dependencies up to date and Socket runs on those updates to ensure there are no supply chain issues.
- Nano IDs are used throughout the product, following PlanetScale’s example of URL friendly IDs.
- PreviewJS allows us to preview React components in VS Code.
- Retool is our admin panel. It’s amazing how flexible it is, and interfaces directly with our APIs to allow us to manage the system. I definitely wouldn’t want to build our own admin panel when Retool can manage it all for us.
- Swimm manages all our code documentation. There are some complex flows, such as how matches are scored, which are documented using Swimm. It keeps the docs next to the code and the VS Code extension surfaces useful notes in-line. Critically, it highlights out of date snippets and offers auto-fixes as part of PRs. Swimm were a previous sponsor in the Console newsletter (but have not paid for this mention). We also use Eraser (we're an investor) for sequence diagrams inside the docs.
- Trunk is used as a universal linter to ensure we’re not doing silly things like storing secrets in code. However, it’s mainly useful to catch subtle issues like unused variables or unchecked errors. We’re also an investor.
We’re just getting started with Jobs Console, so our goal was to keep the tech stack as simple as possible. It all runs locally on my laptop (MacBook Air M1) and has minimal third-party dependencies.
Using fully managed services like Cloud Run and Cloud SQL mean we’re not responsible for running actual servers. If something breaks in the underlying infrastructure, it’s Google’s responsibility to fix it. The high availability database configuration and deploying Cloud Run across zones means we’re able to deal with most outages automatically.
This means we’re still close to the goal of running no servers I set when starting Console. Jobs Console is an actual product with code and a database, which means it’s much more complex than a newsletter! However, we’ve still tried to keep complexity to a minimum and avoid running things ourselves.
It’ll be interesting to see how this stack evolves.