Skip to content

Architecture and Technical Decision-Making

Architecture at a consultancy is fundamentally different from architecture at a product company. We rarely start with a blank canvas. More often we arrive in an organisation with an existing technology landscape, established teams, inherited constraints, and decisions made years ago by people who have since moved on. Our job is to understand that context, work within it where it makes sense, challenge it where it does not, and leave behind systems that the client's own teams can confidently maintain, extend, and evolve long after our engagement ends.

That responsibility shapes how we think about architecture. We favour approaches that are easy to reason about over those that are clever. We document our decisions so that the people who inherit the system understand not just what we built but why. And we treat architecture as something that happens continuously throughout delivery, not as a phase that produces a document and then falls silent.

Our architecture principles

These principles guide our technical decision-making across engagements. They are not rigid rules. Different contexts demand different trade-offs, and a good architect knows when to deviate from a principle and can explain why. But these represent our defaults, the positions we start from unless there is a compelling reason to do otherwise.

Design for the team that comes next

Every system we build will eventually be owned by someone who was not in the room when the decisions were made. This is especially true in consultancy, where the handover is a known, planned event rather than an unlikely future. We design with that team in mind. We choose technologies the client can recruit for and support. We write code that is readable rather than merely concise. We favour established patterns over novel ones where both would solve the problem equally well. The cleverest solution is rarely the best one if the people maintaining it cannot understand it.

Prefer simplicity

Simple systems are easier to build, test, deploy, operate, debug, and change. Complexity should be introduced only when simpler alternatives have been genuinely evaluated and found insufficient. A modular monolith that a small team can reason about is often a better starting point than a distributed microservices architecture that demands platform engineering capability the client does not yet have. We earn the right to add complexity by first demonstrating that the simpler approach will not meet the need.

Make reversible decisions quickly, irreversible ones carefully

Not all decisions carry equal weight. Choosing a logging library is easily reversed. Choosing a cloud provider or a primary database engine is not. We calibrate the rigour of our decision-making to the cost of getting it wrong. Low-stakes, easily reversible decisions should be made quickly to maintain delivery momentum. High-stakes, difficult-to-reverse decisions deserve proper analysis, wider consultation, and formal documentation through an Architecture Decision Record.

Secure by design

Security is a design concern, not a compliance checkbox. We consider threat models during discovery, build security controls into the architecture from the outset, and treat security testing as a continuous activity throughout delivery rather than a gate at the end. For UK public sector engagements, we align with the NCSC Cloud Security Principles and design systems that support the shared responsibility model for cloud services. Secrets are managed through proper vaulting, access is controlled through least-privilege principles, and data classification informs how and where information is stored and transmitted.

Cloud-native by default, pragmatic by necessity

We design for the cloud because that is where most modern systems run and where the operational advantages are greatest. We use managed services where they reduce operational burden without creating unacceptable vendor lock-in. We containerise workloads for portability and consistency across environments. But we do not adopt cloud-native patterns dogmatically. If a client's constraints mean a hybrid approach is the right answer, or if a particular managed service creates more lock-in than it saves in operational effort, we adjust. The architecture should serve the client's situation, not an ideology.

Build for observability from the start

A system that cannot be observed in production cannot be operated reliably. We design logging, metrics, tracing, and health checks into the architecture from the beginning rather than retrofitting them when the first incident reveals we are flying blind. Structured logging, correlation identifiers across service boundaries, and meaningful health endpoints are standard expectations, not optional extras.

Favour proven technology for core systems

We are not early adopters for the sake of it. Core systems that handle critical business logic, data persistence, and user-facing transactions should be built on technology with a strong track record, active community support, and a deep talent pool. Innovation and experimentation belong at the edges, in prototypes, in tooling, in areas where the cost of failure is low and the potential upside justifies the risk. This does not mean we avoid modern technology. It means we distinguish between technology that is modern and technology that is immature, and we are honest about which is which.

Architecture in the delivery lifecycle

Architecture is not a phase. It is a thread that runs through every stage of an engagement, from the earliest conversations about the problem through to handover and beyond.

During discovery

The most important architecture work often happens before a single line of code is written. During discovery we focus on understanding the existing landscape: what systems are already in place, how they integrate, where the pain points are, and what constraints are genuinely fixed versus merely assumed. We map out the non-functional requirements, the performance expectations, regulatory requirements, security considerations, and integration points, because these shape the architecture far more than functional requirements do.

Discovery is also where we identify the highest-risk architectural decisions and begin to work through them. If the engagement depends on a particular technology choice or integration approach working, we would rather discover that risk early through a spike or proof of concept than encounter it halfway through delivery when the cost of changing course is much higher.

During delivery

Architecture evolves as the system is built and understanding deepens. We do not attempt to design everything upfront because we have learned that the most useful architectural insights emerge through the act of building. Instead, we maintain a lightweight set of architecture documentation that evolves with the system and we use Architecture Decision Records to capture significant choices as they arise.

The architect, or whoever is fulfilling that role on a given engagement, is embedded in the delivery team rather than operating at a distance. They participate in sprint ceremonies, review pull requests, pair with developers on complex problems, and keep the broader architectural vision aligned with what is actually being built. Architecture that exists only in a slide deck and has diverged from the code is worse than no architecture documentation at all, because it actively misleads.

At handover

Handover is where the investment in documentation and decision-recording pays off most visibly. A well-maintained set of ADRs, a clear C4 context diagram, and an up-to-date record of integration points and operational concerns give the receiving team a running start. We plan the handover from the beginning of the engagement, not as an afterthought in the final sprint. If the client's team will own the system, we involve them in architectural discussions throughout delivery so that there is no gap in understanding when we step away.

Architecture Decision Records

Architecture Decision Records are one of the most valuable tools in a consultancy's toolkit. They capture the context, options considered, and rationale behind significant technical decisions in a format that is lightweight enough to actually be maintained and detailed enough to be useful months or years later when someone asks "why did we do it this way?"

Why we use them

In a product company, the people who made a decision are often still around when someone questions it. In consultancy, they frequently are not. ADRs bridge that gap. They give the client's team a clear record of what was decided, what alternatives were considered, and why the chosen approach was preferred. They also provide an audit trail that is increasingly valued in public sector engagements, where the GOV.UK Architectural Decision Record Framework encourages consistent documentation of technology decisions across government.

Beyond handover, ADRs improve the quality of decision-making in the moment. The discipline of writing down the context, constraints, and trade-offs forces a rigour that purely verbal discussions often lack. Decisions that seemed obvious in a meeting room sometimes look quite different when the reasoning has to be articulated clearly enough for someone else to follow.

When to write one

Not every decision needs an ADR. We write them for decisions that are significant, meaning they are difficult to reverse, affect the system's structure in a material way, involve meaningful trade-offs between competing concerns, or are likely to be questioned in future. Typical triggers include choosing a primary technology or framework, defining the integration approach with an external system, selecting an authentication or authorisation strategy, deciding between architectural patterns such as synchronous versus asynchronous communication, and establishing the deployment and hosting model.

If a decision can be easily changed later without significant rework, it probably does not need an ADR. If the team is likely to forget why a decision was made within six months, it probably does.

Our template

We keep the format simple. An ADR that is too burdensome to write will not get written. Our standard template covers the following sections.

Title and date. A short, descriptive title and the date the decision was made. We number ADRs sequentially within each project so they form a chronological record.

Status. One of proposed, accepted, superseded, or deprecated. This makes it clear whether a decision is still current.

Context. The situation that prompted the decision. What problem are we solving? What constraints are we working within? What are the relevant non-functional requirements? This section is arguably the most important because it captures the information that is most likely to be lost over time.

Options considered. A brief description of each viable option, with its trade-offs. This section demonstrates that the decision was deliberate rather than default, and it helps future readers understand what was ruled out and why.

Decision. What we decided and a clear statement of the rationale. This should be specific enough that someone who was not involved can understand the reasoning.

Consequences. What follows from this decision, both positive and negative. What becomes easier? What becomes harder? What new constraints does this create? Honest articulation of the downsides builds trust in the record's integrity.

This structure aligns with the widely adopted format originally proposed by Michael Nygard and is compatible with the GOV.UK ADR Framework used across the UK public sector.

Where they live

ADRs live in the project wiki in Azure DevOps rather than buried in the code repository. The wiki is the natural home because it is immediately visible to everyone on the project, including client stakeholders who may not routinely browse the repo, and it does not require a pull request to read or update. We typically create an "Architecture Decisions" section in the project wiki with each ADR as a separate page, numbered sequentially. Since Azure DevOps wikis are backed by a Git repository under the hood, the version history and diff capability are still there when needed. ADRs are written in Markdown, which renders neatly in the wiki and can be exported or migrated if the project moves to a different platform.

Documenting architecture

Good architecture documentation is the minimum set of information someone needs to understand, operate, and change a system. We aim for documentation that is lightweight enough to be maintained as the system evolves and specific enough to be useful when it matters.

The C4 model

We use the C4 model as our default approach to visualising software architecture. Created by Simon Brown, C4 provides four levels of abstraction, from the broadest system context down to individual code structures, letting us communicate the architecture at the right level of detail for the audience.

Context diagrams show the system in its environment: who uses it, what other systems it interacts with, and where the boundaries are. These are useful for stakeholders, governance boards, and anyone who needs to understand the system without getting into technical detail. Every project should have one.

Container diagrams show the major technical building blocks: web applications, APIs, databases, message queues, and the like. They convey the high-level technology choices and how the components communicate. This is the level that most developers and architects find most useful day to day.

Component diagrams show the internal structure of a container. These are useful during detailed design but can quickly become stale if maintained manually. We produce them when they add genuine value and accept that they may represent a point-in-time view rather than a living document.

Code-level diagrams are rarely worth producing manually. If the codebase is well structured, the code itself serves as this level of documentation. IDE tools and automated generation can supplement where needed.

Diagrams as code

We prefer diagramming tools that treat diagrams as code rather than as binary files locked inside a proprietary application. Our default choice is Mermaid, which renders natively in Azure DevOps wikis, MkDocs, and most other Markdown platforms without needing any additional tooling or build steps. Mermaid covers the diagram types we reach for most often in practice: flowcharts, sequence diagrams, C4 context and container views, and entity relationship diagrams among others. It can also be rendered to PNG, SVG, and PDF when diagrams need to appear in documents or presentations.

The real advantage of writing diagrams in Mermaid rather than drawing them in Visio or draw.io is that changes are tracked through version control in the same way as any other text file. Reviewers can see exactly what changed in a diff without having to open two versions of a diagram side by side and squint at the differences. There is no fiddling with box positions and arrow routing when the only thing that actually changed was an added dependency between two services. The tooling gets out of the way and the content stays front and centre.

Working with public sector clients

Many of our clients operate within the UK public sector, where architecture decisions are guided by established frameworks and standards. We are familiar with these frameworks and design our approach to align with them, which reduces friction for our clients and demonstrates that we understand the environment they operate in.

The Technology Code of Practice

The GOV.UK Technology Code of Practice sets out thirteen criteria that guide how government organisations design, build, and buy technology. Several of these have direct implications for architecture: designing for interoperability, using open standards, avoiding vendor lock-in, making source code open where appropriate, and ensuring technology choices are sustainable. We ensure our architectural recommendations are compatible with these principles, and where a client is subject to Cabinet Office spend controls, we structure our documentation to support the assurance process.

NCSC Cloud Security Principles

The National Cyber Security Centre's fourteen Cloud Security Principles form the baseline for evaluating cloud service security in the UK public sector. When we design cloud architectures for government clients, we assess our approach against these principles, covering data protection in transit and at rest, supply chain security, operational security, and personnel security among others. For clients procuring services through G-Cloud, alignment with NCSC principles is expected rather than optional.

The GOV.UK Service Standard

Where the system we are building constitutes a public-facing service, it will typically need to meet the GOV.UK Service Standard. Several points in the standard have architectural implications, including designing for accessibility, building a multidisciplinary team, iterating based on user research, and choosing the right tools and technology. We factor these requirements into our architecture from the outset rather than treating them as constraints to be addressed retroactively.

Common trade-offs we navigate

Architecture is the art of managing trade-offs. Every decision involves giving something up to gain something else, and the skill lies in making those trade-offs deliberately and transparently. These are some of the trade-offs we encounter most frequently.

Build versus buy. Building bespoke software gives maximum control and fit but carries ongoing maintenance cost. Buying or adopting a managed service reduces maintenance burden but introduces dependency and may not fit the exact need. We evaluate this trade-off on a case-by-case basis, leaning towards buying where the capability is not a competitive differentiator for the client and building where the specific requirements are genuinely unique.

Consistency versus autonomy. In larger systems with multiple teams, there is a tension between enforcing consistency of technology choices and allowing teams the autonomy to pick the best tool for their context. Too much consistency and teams are forced into suboptimal choices. Too much autonomy and the organisation ends up with an unmanageable zoo of technologies that nobody can recruit for or support. We generally advocate for a "paved road" approach: a supported, well-documented default stack that teams should use unless they have a compelling reason not to, with a lightweight governance process for exceptions.

Upfront design versus emergent architecture. Designing everything before building anything risks producing an architecture that does not survive contact with reality. Designing nothing risks ending up with accidental architecture shaped by expedient short-term decisions. We aim for the middle ground: enough upfront thinking to establish the major structural decisions and reduce risk, combined with the flexibility to adapt as understanding deepens through delivery. ADRs are the mechanism that makes this work, capturing decisions as they emerge rather than requiring them all to be made at the start.

Optimising for now versus optimising for later. Systems can be over-engineered for a future that never arrives or under-engineered for a present that quickly becomes unsustainable. We try to build for current known requirements with a clear understanding of likely future directions. This means designing interfaces and boundaries that will accommodate growth without building the growth capability itself until it is needed. The principle of "make it easy to change" is more durable than "predict the change".

Architecture is a conversation

The most effective architecture work we do happens through ongoing dialogue with delivery teams, stakeholders, and the client's own technical leadership. A brilliant architecture that nobody understands or agrees with is worth less than a good-enough architecture that everyone can work with.