Back to thoughts

Build an Architecture That Works for You

Your architecture is a product, and the team that builds and runs it is the user. Design it for them first.

Usability beats purity

Would you rather meet a standard to the letter, or build something people can actually use? Reaching REST Level 3 or adopting GraphQL matters far less than whether someone can integrate with your API without reading a manual. Steve Krug's Don't Make Me Think is about web interfaces, but it applies just as well to APIs, packages and any surface other people have to touch.

We engineers tend to favour what's technically interesting over what's easy to use. I see people argue about curly braces on if statements while the API itself makes no sense. Code quality matters, but it never matters more than software that is easy to use and hard to misuse. (More on that in a later article.)

If you're designing a customer-facing API, start with three questions:

  • What language do your users speak? Make the API speak it too.

  • What do they need to do? You don't need every action, but know the main ones and structure the API around them.

  • Do they need results straight away? That decides between synchronous and asynchronous.

I over-corrected, and it cost us

In 2018 we added what was effectively a second app to our frontend, because our deployment patterns left us no better option. It brought dependency and complexity problems, so we reacted by isolating everything: one responsibility, one repository. The effects were lasting and took several years to unwind.

Then we noticed we were opening five pull requests across five repositories to make a simple change. The slowdown was real, it took whole sprints to do thing that should have taken days. So we consolidated. Today the same change is one pull request in the backend and one in the frontend.

Each decision made sense on its own. The mistake was answering a complexity problem with more structure. More structure rarely fixes complexity.

Every giant started small

It's tempting to design for the scale you hope to reach. But the companies we hold up as examples of huge, scalable architecture didn't begin that way. Twitter started as a Ruby on Rails application and rebuilt major parts of its backend as it grew. Instagram served millions of users with a tiny team on Django. Shopify still runs one of the largest Rails applications in the world, as a modular monolith. Netflix began as a DVD-by-mail business and moved to the cloud after a major database outage in 2008.

They all started on simple, well-understood platforms that let a small team ship quickly, and they evolved when real load demanded it. Their complexity was earned, not pre-paid. Copy the end state of a company with ten thousand engineers when you have ten, and you pay for problems you don't have yet.

Build for the team you have

Every extra service, language or framework is something your team has to understand, operate and be woken up for. A system a small team can hold in their heads will outpace a theoretically better one nobody fully understands. Our five repositories were exactly that: tidier on paper, harder for the people working in them.

Three questions before you adopt anything

  1. Who is going to use this?

  2. Can the team we have build and run it?

  3. What is the simplest thing that works today, and how will we know when it's time to evolve it?

Start simple. Earn your complexity.