Every language, database and service in a system is a commitment someone must honour for years: patch it, pay for it, find someone able to read it. Stack selection is a risk decision taken with the client.
Where no stack is prescribed, we work through six questions in order. Any one of them can disqualify a technology that looked attractive on paper.
We begin with what already runs: the ERP version, the reporting tool finance trusts, the internal Windows skills, the maintenance windows that genuinely exist. A superior database is a poor choice where nobody could restore it at two in the morning.
Each component adds patch cycles, credential rotation, capacity planning and alert triage. We ask who performs that work after handover and how much they can absorb. Where the answer is nobody, a managed service is correct even at a higher unit price, because unmaintained infrastructure fails quietly.
Australian privacy obligations, sector rules and contractual commitments decide location, and they outrank engineering preference. We keep data, backups and logs in Australian regions and check monitoring and error-tracking providers for their own storage geography.
Choosing a technology also chooses who maintains it later. We weigh release cadence, upgrade paths, library maturity and the availability of Australian contractors, and say plainly when a narrower tool narrows the hiring pool.
Licence fees are visible and rarely the largest item. A managed service frequently beats self-hosting once engineer hours are counted, while a chatty serverless design can cost more than two small containers running continuously.
We always ask how hard it would be to hand the system to another firm in five years. That favours portable blocks: databases with standard dumps, containers from common base images, infrastructure expressed in Terraform, interfaces described in OpenAPI. It argues against logic trapped in a vendor workflow engine.
What we run today, what we have operated long enough to understand its failure modes, and what we would propose again.
Python, C#, Node, PHP and Go, the conditions that make each correct, and the languages we decline.
Backend detail →Interface frameworks, bundle budgets, and when server rendering beats a single-page application outright.
Frontend detail →AWS and Azure services, Terraform, containers and pipelines, plus what we decline to operate.
Cloud detail →Relational engines, document stores, caching and object storage, judged on how fast they can be restored.
Database detail →Testing, static analysis, tracing and handoff discipline, treated as deliverables.
Tools detail →Given no constraint from an existing estate, this is our starting position, the alternative that displaces it, and the situation that makes the alternative correct.
A typed front end rendered on the server wherever time to first byte or search visibility matters. Server-rendered templates win for internal tools and poor connectivity. A client-side model earns its cost only when the interface behaves like an application.
Versioned JSON described in OpenAPI with idempotent writes, since consumers are usually other companies' software. GraphQL replaces it when clients need different shapes from one dataset. Messaging replaces it when work must survive an outage.
PostgreSQL by default, for indexing, replication and absence of licence cost. SQL Server where licences already exist or reporting depends on it. Document storage joins only for shapeless payloads such as captured webhook bodies.
Standard images on the provider's container platform, keeping the operational surface small and the exit route obvious. Serverless handles the edges: scheduled jobs, webhook receivers, image processing. Running everything as functions pushes connection limits onto every later change.
Much of our work is not greenfield: C#, PHP extended by whoever was available, Java nobody has rebuilt, VB.NET desktop tools, Classic ASP still taking payments, FileMaker databases covering what the ERP never did. Almost none of it justifies a rewrite.
Source into version control, an environment rebuildable from nothing, and a map of everything touching the system. The output is a written register naming what fails first.
Verified restores rather than assumed backups, monitoring on the jobs that matter, dependencies brought forward, and deployment recorded well enough for someone other than its author.
New capability is built outside the legacy application and connected through a defined interface, so fresh work never deepens coupling.
Old and new run side by side, reconciled daily until they agree. Traffic moves after that, with a rollback path held open. Retirement comes when the last dependency is gone.
They apply to every system we deliver, small internal tool or customer-facing platform alike, built in rather than quoted as extras.
Role-based permissions enforced at the interface and again at the API layer, so a hidden control is never the only protection. Every read and change is logged with actor and time.
Transport security enforced everywhere including internal hops, encryption at rest across databases, objects and backups. Data, backups and logs stay in Australian regions.
Builds resolve dependencies against vulnerability feeds and fail on a high-severity finding, with patch windows agreed per severity in writing.
Snapshots and logical dumps with retention agreed in writing, plus a scheduled exercise loading a copy into an isolated environment.
These are standing positions rather than negotiating tactics. Each has cost us work.
Two platform builds double the maintenance burden for an interface a responsive web application already delivers, unless the job genuinely needs offline storage on site.
An administration screen used by nine people does not need a client-side framework and a hydration strategy. Server-rendered pages ship sooner and break less.
A cluster nobody maintains is downtime waiting on an expired certificate. Five services belong on the managed platform with a documented rebuild.
We do not replace a working system wholesale, and we do not run a database that cannot be restored inside its stated recovery window.
If nobody can answer that plainly, it is worth examining. We will give our reasoning and our reservations.