Skip to content
Forged Damien Murphy
Back to posts

Engineering for outsized impact with small teams

12 min read

Small teams force better platform decisions

Platforms promise leverage.

A relatively small group of engineers can build capabilities that improve the work of hundreds of developers. Shared infrastructure can be operated once rather than repeatedly rebuilt. Security controls can be embedded into common delivery paths. Difficult operational problems can be solved centrally, then exposed through a simple interface.

That leverage is real, but it is not automatic.

For much of the past five years, I have built and operated internal platforms with teams of three or four engineers.

Later, I became responsible for several platforms with a team of six or seven, but the effective team behind any individual platform usually remained no larger than three people.

These were not experimental services with forgiving reliability expectations. They supported critical development workflows, carried high availability requirements, and were expected to set the standard for reliability and usability across the organisation.

A small team did not reduce what we could achieve.

It changed what we could afford.

When capacity is constrained, architecture cannot be judged only by whether a system can be built. It must also be judged by whether that system can be operated, maintained, upgraded, supported, secured, and evolved by the same small group of people over several years.

That constraint shaped nearly every important decision we made.

The real limit is operational load

Small teams are often treated mainly as a delivery constraint.

The assumption is that fewer engineers means fewer features, slower development, or a smaller platform. That is sometimes true, but it misses the more important limit.

The real limit is recurring operational load.

A small team can build a surprisingly sophisticated service. The danger is building one that creates more ongoing work than the team can sustainably absorb.

Every manual provisioning step, fragile integration, noisy alert, bespoke user configuration, difficult upgrade, or ambiguous support process creates a continuing claim on the team’s attention.

Individually, each task may look manageable. Together, they consume the capacity needed to improve the platform.

This is how platforms become trapped.

The team spends so much time operating what already exists that it can no longer meaningfully evolve it. Reliability begins to decline. Upgrades are postponed. Documentation becomes stale. User requests accumulate.

Engineers start compensating through personal effort instead of improving the system.

For a small platform team, maintenance is not something to consider after the architecture is complete. It is one of the primary architectural concerns.

A design that requires frequent human intervention is not merely inconvenient.

It is structurally incompatible with the team expected to operate it.

Self-service is a capacity-control mechanism

Self-service is often presented as a user-experience feature.

It is certainly valuable to developers. They can access a capability when they need it, without waiting for another team to process a ticket or perform a manual configuration.

For a small platform team, however, self-service is also a survival mechanism.

If every new user requires direct intervention, adoption creates operational work in proportion to success. The more useful the platform becomes, the more pressure it places on the team behind it.

That is the opposite of leverage.

A platform should absorb growth without requiring equivalent growth in its operating team.

This means common actions must be exposed through stable, documented interfaces that users can operate themselves. Onboarding, configuration, credentials, access requests, routine changes, and common troubleshooting steps should not depend on a platform engineer being available.

Where configuration is required, it should usually be expressed declaratively and reviewed through familiar engineering workflows.

For our platforms, templated configuration and pull-request-based changes became particularly valuable. Users could start from a known-good structure, propose a change, and receive feedback through a controlled review process.

This reduced repetitive communication. It made changes visible and auditable. It allowed policy to be enforced automatically. It created a shared history of why a configuration changed.

It also moved interaction away from private messages and one-off instructions into a reusable system.

The important principle was not that every interaction had to be fully automated.

It was that the team should not repeatedly solve the same problem by hand.

Choose technology by total operational cost

Technology choices look different when the people choosing a system are also responsible for operating it.

A technology may be powerful, flexible, and technically impressive while still being the wrong choice for a small team.

Every component introduces more than its immediate functionality. It also introduces upgrades, security patches, failure modes, observability requirements, specialist knowledge, and integration work.

The relevant question is not simply:

Can this technology solve the problem?

It is:

What permanent operational responsibility are we accepting by adopting it?

For Kubernetes-based services, managed EKS became the obvious foundation for us.

We still retained responsibility for workloads, configurations, integrations, security controls, upgrades, and the surrounding operating model. But we avoided taking responsibility for operating the Kubernetes control plane itself.

That boundary mattered.

Managed services are not free of operational complexity, nor do they remove the need for deep technical knowledge. They reduce the number of system layers for which the team holds direct responsibility.

With a small team, that is often the correct optimisation.

Owning fewer layers allows engineers to spend more attention on the parts of the system that create distinctive value: the platform interface, reliability, security model, developer experience, and integration into the organisation.

The same logic applies beyond cloud services.

Prefer established technologies where their behaviour is well understood. Minimise unnecessary variation between platforms. Standardise deployment, monitoring, access, configuration, and incident response where possible.

Novelty spends operational capacity.

It should be reserved for problems where it creates a meaningful advantage.

Safe upgrades must be designed in

Upgrades are one of the clearest examples of hidden platform cost.

Installing software is usually easy. Keeping it current without disrupting users is considerably harder.

When a service is heavily relied upon, every upgrade carries risk. A change may affect stored data, integrations, authentication, performance, configuration, or behaviour that users have unknowingly come to depend on.

A small team cannot afford for each upgrade to become a high-stakes production event.

For several of our services, blue-green deployment patterns became effectively mandatory.

Rather than modifying the active environment in place, we could deploy and validate a new environment while the existing service continued operating.

This changed the nature of an upgrade.

Problems could be investigated before users were moved onto the new version. Integrations could be tested against the candidate environment. Rollback could be achieved by redirecting traffic rather than reconstructing the previous state under pressure.

Blue-green deployment introduces its own complexity, particularly for stateful services. But it transforms many upgrade failures from incidents into pre-production engineering work.

That distinction is enormously valuable.

A small team has limited capacity for emergency recovery. It is almost always better to invest that capacity in reversible change than to repeatedly rely on engineers recovering quickly when an irreversible change fails.

Reliability creates adoption capacity

Internal platforms depend on trust.

Developers choose whether to build critical workflows on top of them. Even where a platform is the recommended organisational standard, reluctant users can delay adoption, create parallel solutions, or treat the service as an unreliable external dependency.

Once trust is lost, recovering it is expensive.

Every new adopter must be persuaded that previous problems have been resolved. Engineers may need to provide extra assurances, support migrations more closely, or explain why the platform should now be considered dependable.

A small team rarely has the capacity for that rehabilitation work.

Reliability therefore does more than prevent downtime.

It reduces the organisational energy required to sustain adoption.

Our managed runner platform has supported more than 150 development teams while maintaining close to 100 percent infrastructure availability.

Our managed SonarQube service has similarly delivered close to 100 percent service availability while supporting more than 30 teams.

The numbers matter, but the more important outcome is behavioural.

Developers generally do not need to think about whether these services will be available. They use them as dependable parts of their delivery systems.

The platform team is not repeatedly asked to justify the platform, defend its reliability, or help teams build fallback mechanisms around it.

That trust creates capacity for both sides.

Developers can focus on their products. The platform team can focus on improving the service rather than continually re-establishing confidence in it.

Reliability is not merely an operational metric.

It is one of the mechanisms through which a small team preserves its leverage.

Observability must reduce uncertainty

Observability is essential for any production service, but a small team cannot afford observability that demands constant attention.

A monitoring system that produces frequent low-value alerts is not making the platform safer. It is continuously taxing the attention of the people responsible for it.

The goal is not to collect the largest possible volume of telemetry.

It is to reduce uncertainty when something important changes.

Useful monitoring should help answer a small number of operational questions:

Alerts should be tied to actionable conditions. Dashboards should make abnormal behaviour visible without requiring extensive interpretation.

Logs and traces should support diagnosis when an incident occurs, but the normal operation of the platform should not require engineers to continuously inspect them.

High signal and low noise are not cosmetic preferences.

They determine whether a small team can maintain situational awareness across several services without becoming permanently reactive.

Every incident should reduce future work

Incidents expose the difference between how a system was expected to behave and how it actually behaves under pressure.

For a small team, that information is too valuable to waste.

Each significant failure should lead to more than an immediate repair. It should reveal an assumption, missing guardrail, weak dependency, unclear ownership boundary, inadequate alert, unsafe deployment process, or recovery path that was never properly tested.

The incident is not fully resolved when the service is restored.

It is resolved when the system has been changed so the same class of failure becomes less likely, less damaging, easier to detect, or easier to recover from.

That does not mean every incident requires an elaborate retrospective or a large programme of corrective work. The response should remain proportional.

But there should usually be a durable result:

The objective is to convert operational pain into future capacity.

A team that repeatedly resolves incidents through individual heroics remains fragile.

A team that turns incidents into system improvements becomes progressively harder to disrupt.

Vendor release cadence is part of your architecture

Not all operational cost originates inside the platform.

One growing source of pressure for us has been the release cadence of upstream software vendors. Many providers have shifted towards scheduled monthly releases.

At first glance, frequent releases appear beneficial. Updates arrive quickly, vulnerabilities can be addressed sooner, and users receive a steady stream of improvements.

The cost is transferred to operating teams.

Each release must be reviewed. Changes need to be understood. Compatibility and security implications must be assessed. A decision must be made about whether to upgrade, defer, or skip the version.

Where an upgrade is justified, the team must execute and validate another deployment cycle.

Semantic versioning once provided a useful prioritisation signal. Major and minor releases offered an initial indication of the likely significance of a change.

Teams could then examine the change set and decide whether the value justified the operational effort.

Calendar-based releases weaken that signal.

A monthly version may contain major behavioural changes, minor enhancements, security fixes, or little that matters to the platform operator. The release number itself communicates less about urgency or risk.

For a team managing several platforms, the cumulative assessment burden becomes substantial.

This is a reminder that dependency selection is also capacity planning.

When evaluating software, teams should consider not only its features and architecture, but the operational contract implied by its release policy:

A vendor’s delivery model becomes part of your platform’s operating model, whether or not you consciously design for it.

Standardise the platform behind the platforms

When one team owns several services, some of the greatest leverage comes from standardising how the platforms themselves are operated.

Each platform will have its own domain and technical characteristics, but many operational concerns are shared:

Solving each of these independently for every platform creates unnecessary variation and multiplies maintenance.

Shared patterns reduce that cost.

A common deployment model means engineers can understand a new service more quickly. Standard dashboards and alerts reduce cognitive switching. Reusable infrastructure modules make improvements portable.

Common runbook structures make operational knowledge easier to find. Similar blue-green deployment mechanisms allow lessons from one platform to strengthen another.

This does not require forcing every service into an identical architecture.

Standardisation should concentrate on recurring operational problems where variation adds little value.

The objective is not uniformity for its own sake. It is to ensure that the team’s knowledge, automation, and improvements compound across the portfolio.

The platform team should itself have a platform.

Use constraints as design guidance

Small teams have genuine limitations.

They cannot support unlimited customisation. They cannot manually onboard every user. They cannot own every layer of the stack. They cannot treat maintenance as work that will somehow be handled later.

They cannot absorb an unlimited number of alerts, upgrades, incidents, integrations, and special cases.

These constraints can be frustrating, but they are also clarifying.

They force questions that larger teams can sometimes postpone:

These questions usually produce better systems.

A small team can build and manage platforms with extremely high reliability and broad organisational impact. But it cannot achieve that through effort alone.

The team must design for leverage at every layer.

Self-service prevents adoption from becoming support work. Managed services reduce the number of layers the team must operate. Automation removes recurring manual effort. Reversible deployments constrain upgrade risk.

High-signal observability protects attention. Standardisation allows improvements to compound. Incidents are converted into permanent system improvements. Reliability protects the trust on which adoption depends.

Small teams do not need smaller ambitions.

They need architectures that respect the value of their attention.


Share this post:

Previous Post
Securing GitHub Actions Is a Hard Problem
Next Post
I Never Meant to Build a Personal Cloud