Architecting like Google
This year I have written a lot of software:
- A lightweight process orchestrator (replacement for Docker Compose)
- A protocol buffer based ORM, with autogenerated typed SQL RPC for TS and Rust.
- A React Single Page App
- A Bun-based TypeScript backend
- A React Native mobile app
- A Go-based service for iPod sync, which links into ffmpeg and libgpod via CGo headers.
- A Go-based music library indexer with metadata editing, custom MP3 content-hash based indexing (ignoring ID3 metadata).
- An Electron-based version of the MyTunes client.
- A Tauri-based version of the MyTunes client.
- A GTK-based rewrite of the MyTunes client, using native widgets.
- A CEF-based PoC of the MyTunes client.
- An automated visual testing harness / VM for the MyTunes client on Linux, to test its final built .AppImage portability. Using Qemu and Ubuntu.
- An automated visual testing harness / VM for the MyTunes client on macOS.
- A uniform inter-software communications layer via gRPC.
- A uniform build system via Bazel.
- A monorepo setup, wherein data types and services are defined in
protos/, the database and backfill/migration jobs are indb/, the implementation of various services are in their own directories, the application is indesktop-app/orweb-app/, observability is done via Grafana/Prometheus. - An OpenCV package to preprocess plant root PNG's into data, which is then learnt by a neural network to predict plant growth dynamics, a Python-based GUI for visualising results in 2D and 3D.
- A clone of OneNote for Linux and macOS, with a concurrent-safe on-disk format for cross-device usage via Dropboxes.
- A Python client to download data from blood glucose meters over BlueTooth.
- A Rust-based voxel simulator and programming runtime to study plant morphology.
- An app to explore and collate visual grammars.
And I'm leaving stuff out.
As I've built all of this, I've come around to architecting like Google.
What is architecting like Google?
It's an idea around how to do software development when SWE agents are cheap and generally intelligent, aka when you are your own mini-Google.
- Put everything in one monorepo.
- Use Bazel as a uniform build system, where dependencies are clearly defined.
- For each project, prefer protocol buffers and gRPC to communicate between different components - services, frontends, backends, etc. This lets you re-use things that work - Go for when you need C library integration, Rust for when you prefer strong types. It unifies communication and data models.
- For each project, use SQL databases for persistent data. Whether it is on-disk or as a web application's database. Define schemas in protocol buffers as an IDL. Interact using type safe clients. Use schema evolution and backfill jobs, instead of migrations.
- Autogenerate code to make every component rely on end-to-end type safety. That includes type-safe SQL clients to your database models.
- Throw OUT tools that are slow. Rebuild them as native tooling. Lean towards building your perfect framework.
These are general practices I believe they apply at Google already - but now they can apply to you too. And indeed, a lot of organisations are moving over to Bazel as we speak. Because the future is all-too-well defined by complexity arising from just more code.
To illustrate why these choices are effective, let's imagine the exact opposite advice:
- Put every project in a different repo, with different build setup and tooling.
- Do not use a uniform build system. Use bespoke build systems and dependency management for each project.
- For each project, reimplement and redefine an RPC layer. Define RPC routes in RESTful API's. Prefer manual code upgrades and deprecation of fields, over gRPC-like schema evolution. Frontends and backends rely on bespoke scripts to re-import new RPC routes, or "bare naked" RPC route strings for calling different API's as they're deployed.
- For each project, use a variety of systems for persistent data - custom on-disk formats, SQL databases, NoSQL databases, JSON, binary blobs. Define schemas in a variety of languages, some bespoke, some TypeScript annotated. Interact using a variety of ORM's, across a variety of frameworks. Carefully manage database migrations via a centralised set of migration scripts.
- Use ORM's to query data. Potentially manually parse SQL results into user-defined data types. When it comes to analysing data, you have to write bespoke code which parses all of these different schema/table definitions centrally.
- Use the built-in tools of frameworks you have available - Docker Compose, etc. Wait for things at least 1-5s each time. Prolong your iterations.
All of these choices can easily add a 2x to a 10x to your iteration speed when it comes to building software. And they will become enormously more frustrating as AI agents do all of the work, since you introduce many different bottlenecks.
I will show you how I can one-shot a feature from development to production on the mealplanner project.
- "Codex please add a meal times section at the bottom of the logger. It includes breakfast lunch and dinner, with a dropdown for the meal eaten and a time input with a button for current time and another button to clear"
- Adjust the data model - add/remove from protocol buffer definitions
- Regenerate typed RPC client and gRPC types.
- Write + run database backfill jobs
- Adjust the backend - add/remove from the gRPC specifications.
- Implement backend
- Implement frontend
- Test (visually)
- Deploy
It's crazy that this is possible - to go from text to a full feature. The entire thing is end-to-end typed - the database models, reading/writing to the database, the different components (ipod sync, mp3 indexing, the web UI JS process, the Rust app). The entire build system is uniform - the toolchain, the app, split into various directories - protos, database, frontend, backend, indexer-service, ipod-sync-service. Tests for each too.
Deployment is still a basic
deploy.sh script, but that's because I am lazy and this is just for myself. In production, deployment could be a rolling reconciler just like Google, with proper build artifact management, MPM, load balancing, progressive rollout, metric-based (error rate) rollback, etc.I struggle - really struggle - to see how people don't have FUN doing this. I mean, this is ENGINEERING. It's EXCITING. Building systems. Systems that scale, systems that work. Type safety, reusability. TOOLS. Yes, we don't write code anymore. But that doesn't mean the ideas have stopped. If anything, I have so much more leverage in executing the ideas I never really wanted to code and test. I mean look at that list above. LOOK AT IT.
When I needed vertical terminal tabs, I built them.
When I needed not-shit ORM, I built it.
When I needed iPod sync for Linux, I built it.
It's all there.
Life is glorious in its incredible detail.
What's more is just - how incredible it all is. That this is just the first 12 months. We are truely in the inceptive period of building. I'm working off a layman's budget of just 1x Pro LLM subscription. I could have 100x more in the next 2yrs if trends continue.
Already I'm thinking of lots of projects I love to build. I mean, thinking about the iPod. How is an iPod made? What's the frame made out of? What's the PCB board? What does the firmware look like? It is possible to build all of this, much more possible for me than it ever was before. I can build the firmware in a day. I can build the VM to test the firmware's UI visually in 10mins. I can ask and learn how to CNC the metal frame, I can ask and learn how to build the plastic caps, I can ask and learn what components I need to build the electronics. I might fail at the electronics using AI, for now. But for how long?
Why architect like Google?
There are many reasons but what I like to think about is un-bottlenecking the agents.
Every schema change is a bottleneck. So make schema changes async-safe - tables and columns are named using UUID's, where the "human name" is simply a view.
Every RPC change is a bottleneck, waiting for the new service to come online. So make RPC's schema-evolving.
Every review is a bottleneck. So make automated visual, unit, integration, etc. testing the rails.
Every clone, build, toolchain import, etc. is a bottleneck. So make it all one monorepo. Allow it all to happen in flow.
Every remote host is a bottleneck. So make it all file-based. Copy and test files between computers.
At a certain point, I'm skeptical of even using branches. Why do we need branches? Are they not a human artifact of the code review process? One branch per agent. The code is hidden behind a feature flag. Everything is tested in realtime and accessible via other agents.
Summary
Architecting like Google is the final state I've landed on for this month in the singularity. It encapsulates the general idea of the leverage I got access to this year - the ability to develop so many more software projects, with so much more speed, and imbuement of my own judgement of what increases my experience - type safety, autogenerated cross-language bindings, uniformity, etcetera. And sets forward the idea into the future - if Google does it this way, why can't I?
Maybe there is another organisation we can choose here, because to be honest, I am not that keen on implementing a search engine (unless it's Exa for blog posts / human writing, in which case, hell yeah!). I already mentioned "Building Like Apple" as one idea - can you approach building an iPod in the same way as this? If every process was "typed"? If it featured the maximum level of unbottlenecking for agents to do. If we had verifiability at different stages. Would this be possible? But this isn't really what I was going for. What I meant was - what would leverage like Apple's look like in your own hands? Because I've painted a picture for Google. What would the leverage like Apple's look like in your own hands?
I have been thinking about this recently as I've been interested in a biotech startup idea. I wonder if in future, people will start thinking in terms of "how much does a <specialisation> scientist cost in tokens" in the same way we [still|used to] think about "how much does a frontend + backend cost"?