It’s been nine months since I started at Fieldguide. Time really flies by, yet the weird state of our world casts a stagnant shadow on things. With many people fully vaccinated, I’m enjoying more time outside of the apartment and slowly relearning how to socialize.
Joining Fieldguide has allowed me to reflect on many aspects of the product development process. Ideally, one is always trying to continuously improve, but a career switch is a sure way to challenge the status quo. In this blog, I'll share about Fieldguide's technology stack and how code generation affects our iteration speed.
At the core, Hasura generates most of Fieldguide’s boilerplate CRUD endpoints via GraphQL and orchestrates Postgres migrations. The engine exposes an intuitive console GUI, facilities event trigger configuration, and even supports real-time functionality out of the box. In this rather drastic paradigm shift, most of our backend development can be achieved via manual and programmatic configuration changes.
This shift, of course, comes with some tradeoffs. Inherently, the underlying generated Postgres queries and manual review of configuration changes (YAML diffs) can be challenging. We have already improved the latter, including an open-source GitHub Action to generate readable summaries of these changes as pull request comments. While we’ve only scratched the surface with what we can automate with this configuration-driven approach, the efficiency gains are far outweighing the costs.
A surprising amount of functionality can be implemented with boilerplate CRUD, but there will always be a need to implement custom business logic, complex mutations, and interfaces to external services. For these needs, we configure a TypeScript GraphQL server as a Hasura remote schema, effectively extending the set of generated types with custom functionality. With GraphQL Nexus, declarative TypeScript code generates this exposed GraphQL schema extension as well as TypeScript interfaces, used internally to facilitate backend development.
But code generation is not scoped to the backend. Client queries and mutations written in our React application are parsed by GraphQL Code Generator, which generates Apollo Client React hooks and TypeScript interfaces. This results in some intentional coupling: As our data model and schema extensions evolve, generated React hooks accelerate frontend development, and their strict typing de-risks changes across the board.
At first, this coupling might seem problematic. At the very least, it’s a certain deviation from more conventional API development. There is no domain layer, and with that lack of abstraction, changes to the data model directly affect the frontend. This behavior is acceptable for internal APIs and is highly effective when used with strictly typed languages providing compile-time feedback (in this case both TypeScript and GraphQL from which code is generated).
GraphQL is the common thread throughout all of the above, but these efficiency gains come from strictly typed code generation, independent of the query language. GraphQL would be irrelevant without the ecosystem of tooling surrounding it, which has primarily contributed to its popularity. With that said, frameworks such as API Platform enable such workflows for RESTful development, and Hasura is even becoming interoperable with REST APIs.
At the end of the day, it doesn’t matter what technology you use to build software. It's all about the problems you're solving for customers. The faster you can iterate, the easier you can experiment with solutions, and the better chance you can solve the problems at hand.
If this blog piqued your interest, or if you share the same mindset about product development, Fieldguide is hiring!