
Organisations scale and structure their engineering teams in all kinds of ways. From what I’ve seen firsthand — and from closely observing how different engineering cultures evolve, these decisions can shape far more than just headcount. In this post we’ll dive into what that looks like, with some cheesy analogies and real-world lessons along the way. 🧀
Due to the breadth of problems we can solve with software, it naturally becomes the case that these problems come in countless shapes and sizes; you’re assembling a bedside table from IKEA on your own one day to building a sauna that can sprout legs and run with 500 different people the next! 🧖
As such, how we solve these challenges of team scale whilst respecting the hierarchal economics of the company, heavily impacts how we build and ship solutions as engineers, even if you don’t think it does. Conway’s law describes this perfectly:
“[O]rganisations which design systems (in the broad sense used here) are constrained to produce designs which are copies of the communication structures of these organisations.” — Melvin E. Conway, How Do Committees Invent?
Ultimately, you can think of this as “if your engineering team communicates well on a project this will yield a positive deliverable, otherwise it won’t — right?”. Is that a blatantly obvious statement though? The code you produce in its purest artefact ends up becoming a reflection of how well that statement has been respected over a period of time, here’s some examples:
- Do you find yourself marking some of your implementations with TODO: comments to come back to later as tech debt? 💸
- Are you unable to move fast and make meaningful changes? 🏃
- Are end users still finding bugs in features which are finished? 🐛
- If relevant, is it uncommon for your backend and frontend engineers to engage in technical feature discoveries together? 🗣️
- Do you frequently seek questions surrounding the requirements of the thing you’re building? 😵💫
- Are you often blocked implementing something because you’re waiting for another engineer to finish their part? 🚫
If the answer to one or more of these is yes, then it circumstantially suggests a breakdown in team comms somewhere. Oftentimes, company process is unidirectional — a result of top down leadership defining these processes, and you entrusting that leadership will do the best by you to ensure we can all ship software at quality and at pace as effortlessly as possible.
I empathise an extreme amount with deadline pressures more than the next person, and that can be a larger than life symptom to the success of a project. Inevitably you’ll have to cut corners to hit specific deadlines and that’s okay, that being said you can still produce a shippable product that you’re proud of with low technical debt and high quality at pace — but you do it by cutting corners, not complete shapes 🙏 (unless you’re a startup, just ship something 😂).

Going back to Conway, we’re going to explore his law a bit further using two popular methods for scaling an engineering team — horizontally & vertically. Paying close attention to the pros and cons holistically, the importance this plays in the outcomes of the software we build, and more importantly the very experiences we have doing it. Finally, we’ll outline useful takeaways you can implement into your teams along the way if you find yourself in either of these camps, lets dive in! 🏊🏻
Understanding horizontal & vertical scaling ⚖️
Let’s take a look and understand the differences between these two types of scaling techniques used to grow engineering teams across two made up organisations — Nacho Business & Pain in the GlASS! 👀
Nacho Business 🧀
Nacho Business run a nacho focused food delivery app, if you’re a nacho craver you whip open their mobile app and within a few taps you’ll have your favourite nachos oozing with your favourite cheese blend and toppings in under an hour, guaranteed! Sounds yummy right? Well, in order to build such an experience for these cheesy fanatics, they needed a team of engineers first — Nacho Business decided to scale their engineering team horizontally:

As you can see, we have a strong team of 10 engineers ranging across multiple disciplines spread across frontend, backend, mobile, and DevOps. This type of scale is great because we specifically focus the engineer into one discipline and they become incredibly specialised at what they do, as a result you can throw any number of engineers across these teams at a problem to solve and they’ll come up with a solution 9 times out of 10, in theory.
I’ve surrounded each team with a “competency boundary”, these are the bounds the engineer can operate within and inevitably silos them off from the rest of what produces a fantastically engineered product, in this example an engineer working in any one of these teams only ever sees and contributes 25% to the app they’re building.
I’m a bad news first kind of guy. So we’ll do cons prior to the pros 🙌
Why this can be considered bad ❌
- Encourages blocking — If you’re a frontend engineer, you will have to speak to a server through an API to query or mutate data, if that API isn’t complete you’re blocked finishing that feature until that implementation is done outside of UI development. And vice-versa for bug fixes.
- Type safety becomes a nightmare — If the stack is agnostic and you’re not using something like gRPC, anytime frontend consume an API they need to know the exact type information for that endpoint ahead of time, otherwise we might as well be shipping to the blind. The source of truth for these response or payload types must be documented and up to date properly using the preferred tooling both engineering teams are comfortable with (OpenAPI/Postman etc.). Sharing that information with frontend teams should be seamless and should be the absolute source of truth your frontend team relies on when doing any async integrations with the API. Backend maintaining this as a source of truth is so important and must empower trust to the consumer of the API.
- Resourcing bottlenecks — Workload bottlenecks are created meaning if there are issues, other engineers are relied on to solve them if they fall into that area of engineering. This adds monumental pressures to these team members workloads, increasing stress and burnout which could otherwise be alleviated if other engineers had knowledge in that area too.
- Ceilings will be hit — Though focusing entirely on one area of engineering makes you a specialist you’ll eventually hit a growth ceiling to where you’ll just know how to do everything in that one space — no matter how much new tech comes out. You end up doing yourself a disservice if you aren’t constantly wanting to learn more and challenge yourself.
Why this can be considered good ✅
- Less work for the same outcome — The end product will be the same in your users eyes but the road to get there won’t be, there’s less work to do if you’re solely focused on one specific area of that application.
- Easier to hire for — It’s a lot easier to hire for a role if it’s for a specific area of engineering as that talent is more likely to exist narrowly than someone with multiple generalist competencies. Although stack overflows 2024 developer survey says otherwise (this is biased as not all engineers are filling our stack overflow surveys though!).
- Specialism breeds brilliance — If you have an engineer who has only touched one part of the stack for their entire life they’re going to be more competent in that area than an engineer who has dabbled between technologies, and they’ll be obnoxiously good at it.
Pain in The GlASS 🪟
Pain in the GlASS run a window renewal, refurb & maintenance company. If you want some shiny new windows, or have noticed a crack from your weekend antics — you book directly through their web offering to which it’ll send out a window specialist to sort! But in order for users to have a cracking time (…), they need a robust engineering team — Pain in the Glass scale their engineering team vertically:

Now instead, we have 4 total engineers who built the Pain in the GlASS app but they have varying competencies ranging across multiple disciplines and technologies rather than just one. We take a vertical slice of skills instead and prioritise those as opposed to adding more engineers to one specific team and problem.
Now for example, engineer A is able to contribute and is exposed to 100% of the engineering behind the product, other engineers like engineer B sees 75% — overall the amount contributed is a lot more per engineer than it was before in comparison to Nacho Cheese, although there are varying skillsets in terms of how competent each are they’re still able to navigate between these areas and be able to valuably contribute to more than one part of the product.
We’ll switch it up now and do the pros first 🙌:
Why this can be considered good ✅
- Your growth potential is unlimited — If you’re someone who isn’t afraid to dabble across technologies and be uncomfortable, that makes you a way more valuable engineer than someone who only does one type. Your stock as an engineer immediately rises because it shows you want to learn other things outside of what you’re comfortable with, even if you’re only competent in 2 areas of engineering, it shows a willingness to want to learn in other areas too, and that you’re perfectly capable of doing so.
- You can create feature teams — You now have dedicated feature teams who are responsible for entire features end to end, instead of teams that own half a feature each. You can run a database migration and centre a div at the same time.
- Less engineers allow you to do more — The greater an engineering team building a product the harder it becomes to contribute to it, the greater the number of engineers is not equal to getting the product delivered quicker. In fact, in most cases I’d say it’s a net negative overall to the outcome of it.
- It’s cheaper — You could hire an engineer on 70k with ≥=2 specialities or you could hire two with 1 viable competency for 50k each totalling 100k. I think I know which one I’d go for in terms of a cost saving.
Why this can be considered bad ❌
- More things to do — If you own the entire world, it’s going to be harder to manage many spinning plates and pressures can come with that. Though through experience this becomes a lot easier to manage and becomes business as usual.
- Harder to hire for — Sometimes it can be difficult to hire someone who is a jack of all trades master of none, not everyone wants to do multiple things, the engineer needs be somewhat exceptional which can be harder to come by in an industry with such high demand.
- You have to know your sh*t — Building software requires you trust that person can deliver it for the user, and you need much more of it if you have an engineer who can cross cut concerns like this.
Tips & Takeaways 🙏
Tip #1 — Communicate as much as possible
A big theme of this blog post is communication, and it’s a comically underrated skill any engineer needs if they’re wanting to meaningfully progress in their careers. If you’re an engineer who’s fantastic at communicating, that places you at a higher bar than if you can build a Twitter clone in an hour but can’t and won’t talk to people.
Also no question is a dumb one, always make sure when shipping any software you’re communicating honestly with the people in your team. Be realistic about what you can/can’t do, about deadlines, your workload, to your manager, asking for help, talk with different teams. Your work should feel like a sailing boat caught in the wind, not trying to ride a bicycle with square wheels.
Tip #2 — Document your API’s 📃
When scaling teams horizontally, ensure your backend teams are shipping OpenAPI documentation alongside their API regardless if they’re major or minor changes. You don’t need to worry about inviting people to workspaces like Postman or ejecting out of your backend workflow to create collections, it’s accessible right in the browser as an out of the box HTTP client you can deploy and configure per environment you spin up.
It comes with all the right tooling to document your response and payload types per endpoint with minimal config. It can be easily shared to your consumer teams, and there’s pretty great CLI tooling (like Kubb) where your consumer teams can generate whole type definitions and schemas from the schema your backend teams define for ultimate type safety.
Tip #3 — Aggressively validate everything your client consumes 🦺
When scaling teams horizontally, ensure your frontend teams are validating any data the client consumes with a schema validator like Zod (I’m sure there are mobile alternatives too) from async sources with your preferred library — irrespective if this is an internal or external source of data. I couldn’t imagine working solely in the frontend without Zod because it’s simply unsafe and irresponsible to do so. You could have a 10x backend engineering team who document all their API’s and we can type them out perfectly on the client in frontend land, but that’s only at compile time, what about runtime when the app is actually running?
A general rule of thumb should be to never fully trust anything your client consumes. What happens if a change is shipped that completely changes the response of an API endpoint, for example:
{ user: "Silly 🪿" }
is changed to
{ user: { firstName "Silly", lastName: "🪿" } }All of a sudden your user property is no longer a string it’s an object representing the firstName and lastName of the user — we can’t just allow stuff like that to enter the client, this is where our users live so it’s immeasurably important we protect our users world and are confident in doing so.
If not careful you could cause breaking changes to your product and we want to avoid that at all costs. It’s not just your codebase that this impacts — it’s your users, your business, KPI’s, retention rates, everything! By building as many safety nets as humanly possible you’ll go a long way, catching the error at runtime and gracefully handling it then and there is a much better experience than your app breaking because of a silly “Cannot read properties of undefined” error 🤪.
Tip #4 — Empower a bidirectional culture 🤝
If you care a lot about this stuff, you’ll naturally question why things are done a certain way. That’s great, because you open yourself up to being mentored on the “why” part of that equation — you learn something new and you give the opportunity for someone to teach you. There are zero downsides to doing this and I encourage question asking so much.
There’s always a reason for everything but that shouldn’t stop your voice being heard and listened too, we touched a little bit on process being unidirectional but I think you’ll find the best people mould processes to be as open source as possible where necessary, shaping changes according to the needs of their team within reason.
If you think something should be done better, even if you’re a junior engineer — make your voice heard and start championing that change alone or with your peers!
Tip #5 — Be self-aware and kill your ego 💀
I find this to be more common in vertical teams, but can be applied to horizontal ones too it doesn’t really matter. The art of being self-aware about your workload is really important, but taming the ego that comes with it is even more so. Yes, you might be able to operate anywhere in the stack but that doesn’t mean you need to be a smug a**hole about it either, entitlement isn’t a trait that will get you far in this industry and will commonly lead to being humbled if not properly controlled.
Learning to control your ego, especially when you’re starting out is super important as you make the world a better place digitally brick by brick. Always be humble, always be the most stupid person in the room, and always lead with empathy for your fellow teams and engineers alike. Treat others how you’d like to be treat 😊.
Wrapping up 👋
Give me a clap 👏 if you enjoyed! I had a lot of fun leaning into the more philosophical and business side of engineering. Thanks! ☺️