
Most architecture reviews look at the wrong things. They audit code style, count test coverage, and produce a document nobody acts on.
The six things that actually predict whether a system will be cheap or expensive to own:
Can a new engineer run it locally in under a day? The single best proxy for everything else.
Does the data model make sense, and can it be changed? Schema is the hardest thing to fix later.
Where is the coupling, and is it deliberate? Not "is it modular" but "can you change one thing without changing five."
What contracts does it expose, and to whom? Every external consumer is a constraint on future change.
Can you deploy, observe and recover? A system you cannot safely deploy is a system you cannot safely change.
What does the team's own behaviour tell you? Where they are cautious reveals where the danger is.
A proper review takes five to fifteen working days depending on system size, and it should end with a costed remediation plan rather than a list of observations. If the output is a document with no numbers in it, the review has failed.
The same activity, different emphasis, different deliverable. Getting the framing wrong is the most common reason a review disappoints.
Question: does the technology justify the valuation, and what liabilities are hidden in it?
Emphasis: licensing exposure, security posture, key-person dependency, whether the claimed capability actually exists in the code, and what it would cost to keep the product running without the current team.
Deliverable: a risk register with financial estimates, written for people who will not read code.
Time pressure is the defining constraint. You typically get two weeks and limited access. Prioritise ruthlessly: data model, security, licensing, key-person risk. Code quality is almost irrelevant at this stage — you are pricing risk, not craftsmanship.
Question: can we safely extend this, and what do we need to fix before we can move at a normal pace?
Emphasis: operational readiness, undocumented knowledge, dependency currency, deployment safety.
Deliverable: a stabilisation plan with a sequence and a cost, plus an honest estimate of how much velocity will be lost in the first quarter.
This is the most common scenario in our work, and the one where the review pays for itself most reliably. The failure mode without one is quoting a feature roadmap against a system nobody has actually opened.
Question: is the existing system a foundation or a liability?
Emphasis: whether the data model can support where the business is going, how much of the behaviour is documented anywhere, and what proportion of the code is actually used.
Deliverable: a comparison of extend versus rebuild with both costed, including the cost of running both systems during any transition.
Covered in more depth in the rebuild section below, because it is the decision most often made emotionally.
A code review asks is this code good. An architecture review asks what will this cost us to own. They correlate less than people expect.
Beautiful code with an unworkable data model is expensive. Ugly, boring code with clean boundaries and a good schema is cheap. The second is a far better system to inherit, and it reviews badly on every automated metric.
What matters is change cost. Specifically: if the business asks for a typical new feature, how many files, services, teams and deployments does it touch, and what is the risk of it breaking something unrelated?
That question cannot be answered by a linter. It is answered by tracing two or three real, recent changes through the codebase and observing what they touched.
The most useful single exercise in any review: ask the team for the last three non-trivial features they shipped, and read the diffs. Not the code — the shape of the change. If a form field addition touched eleven files across four modules, you have learned more about the architecture than a week of reading would tell you.
Start here, always, before reading a single line of application logic.
Give a competent engineer the repository, the README, and no human help. Time how long until they have the system running locally with realistic data and can make a visible change.
Time to running | What it means |
|---|---|
Under 2 hours | Well maintained. Rare. |
Half a day | Healthy. |
One to two days | Normal for a complex system. Acceptable. |
Three days or more | Serious onboarding cost. Every new hire pays it. |
Requires a specific person's help | Key-person dependency. Treat as a finding. |
This test is disproportionately informative because setup quality correlates with everything else — documentation discipline, dependency hygiene, environment parity, whether anyone has onboarded recently.
A system nobody can run without tribal knowledge is a system where every estimate is a guess, because nobody can safely experiment.
Is the README accurate? Not "does it exist" — does it work as written?
Are dependencies pinned and current? Run the audit tooling. A dependency four major versions behind is not just a security question; it is a signal about maintenance culture.
Are there environment differences that matter? Production on a different database version, or with configuration nobody has locally, means local testing proves less than it appears to.
Can you get realistic test data? A system that only behaves correctly with production data is a system you cannot safely develop against.
The hardest layer to fix and therefore the most important to assess honestly.
The schema itself. Get an ERD, or generate one. Read it before reading code — the data model tells you what the system actually believes about the business, which is frequently different from what the documentation says.
Check the five decisions from our database design guide: primary key strategy, how time is stored, whether history is preserved, where normalisation stopped, and how money and quantities are represented. Each has a characteristic failure mode and each is expensive to correct retroactively.
Specific things worth finding early:
Money stored as a float. Silent, retroactive, affects every row.
TIMESTAMP without a zone, or local times stored as if absolute.
No audit columns anywhere. You cannot reconstruct what happened.
Hard deletes on core entities. No restore, no history.
A JSON column containing what should be six real columns, with three different shapes in it.
Enum values encoded as integers with the meaning only in application code.
Migration history. Read it. It tells you how the schema evolved, whether changes were made carefully or in panic, and whether anyone has ever successfully done a large migration. A migration folder with a six-month gap followed by a burst usually marks an incident.
Can the schema be changed? Does migration tooling exist and work? Has anyone run a backfill on a large table? If the answer is no, the data model is effectively frozen, and that constrains every future feature.
Does the data model support where the business is going, or only where it has been?
A schema modelling a single-tenant product cannot become multi-tenant cheaply. One modelling one country's tax rules cannot become international cheaply. This is where extend-versus-rebuild is usually actually decided, though the conversation tends to happen about code quality instead.
Not "is it a monolith or microservices" — that framing tells you almost nothing on its own. The question is whether the boundaries, whatever their shape, are real.
Trace a change. Pick a plausible feature and map what it would touch. Then compare against what the team says it would touch. Divergence is informative.
Look for the god object. Most struggling codebases have one class or module that everything imports. Find it, measure its inbound dependencies, and note it — it is usually the single largest constraint on parallel work.
Check for cross-boundary database access. In a monolith, does the orders module read the customers table directly, or call the customer module? In a service architecture, does any service read another service's database? Direct cross-boundary data access is the thing that makes boundaries decorative, and it is the specific failure described in our monolith versus microservices guide.
Look for the distributed monolith. If services exist but must deploy together, you have the cost of distribution with none of the autonomy. Check whether any service can genuinely be deployed alone. Ask when that last happened.
Check whether boundaries are enforced or merely documented. A dependency rule not checked in CI is a rule that has already been broken somewhere.
Good boundaries are not necessarily elegant. They are:
Stable — they have not moved much, which suggests they match the domain
Enforced — mechanically, in the build
Aligned to teams — each has an owner who can say yes or no to changes
Data-owning — each side owns its storage
A system with three ugly but genuinely independent modules is easier to work in than one with twelve beautiful, tightly coupled ones.
Every external contract is a constraint on future change, and inherited contracts are frequently undocumented.
Enumerate every consumer. Public API, partner integrations, mobile apps in the wild, webhooks you send, scheduled exports, a read replica someone in finance queries directly, a CSV a customer's system collects nightly. The unglamorous ones are the ones that break.
For each, establish: who consumes it, what version they are on, whether you can contact them, and what happens if it changes.
Check for the mobile app problem specifically. A deployed mobile app is a permanent consumer you cannot update — users on old versions may persist for years. If the API has no versioning and there are mobile clients in the wild, every backend change carries breakage risk. This is a common and underestimated finding.
Look for the absence of a deprecation policy. As covered in our API versioning guide, a system with external consumers and no published policy has no mechanism for retiring anything. Every endpoint ever shipped is effectively permanent until proven otherwise.
Check whether there is a contract specification at all. An OpenAPI spec that matches reality is a strong positive signal. One that does not match is arguably worse than none, because people trust it.
A system you cannot safely deploy is a system you cannot safely change, regardless of how good the code is.
How long does a deploy take, end to end? Over thirty minutes discourages small changes, which is how large risky changes become the norm.
How often do they deploy? Daily is healthy. Monthly means every release is high-risk by construction.
What is the rollback procedure, and has it been used? A documented rollback nobody has executed is a hypothesis.
Is deployment automated, or does someone follow a runbook? Manual steps are where outages originate.
Is there a staging environment that resembles production? If not, every deploy is the first test.
Can you answer "is it working right now" without asking a person?
Are there alerts, and do they fire on things that matter? Alert fatigue is a finding — a channel nobody reads is worse than no alerting, because it creates false confidence.
Is there structured logging with correlation IDs? Without them, debugging a multi-step failure is archaeology.
Are there per-tenant or per-customer metrics in a multi-tenant system? Without them, "it is slow for one customer" is unanswerable.
When was a backup last restored? Not "do backups exist" — when was one last actually restored, and how long did it take? An untested backup is not a backup.
What is the recovery time objective, and has it been measured?
Is there a runbook for the three most likely failures?
Who gets called at 2am, and is that documented?
The most revealing operational question: ask what the last production incident was, and what changed as a result. A team that can answer specifically has a healthy culture. A team that cannot remember either has been lucky or is not looking.
Not a full penetration test, which is a separate engagement. This is a posture assessment that identifies whether one is urgently needed.
The findings that most often matter:
Secrets in the repository. Run a scanner across the full history, not just the current tree. A rotated key that is still in git history is still exposed.
Dependency vulnerabilities. Run the audit tooling. Note both count and age — a critical vulnerability open for eighteen months is a process finding, not a technical one.
Authentication and authorisation implementation. Custom auth is a significant finding. Authorisation checked only in the UI layer is a critical one.
Data at rest and in transit. Encryption status, and where personal data actually lives — including backups, logs, analytics stores and third-party processors.
Access control on production. Who can reach the production database directly, and is it logged?
For regulated domains — healthcare, financial services — check whether compliance is architectural or documentary. A policy PDF with no audit logging, no access controls and no data retention implementation is a finding that will surface at the worst possible moment, usually during a customer's security review rather than a regulator's.
Licensing. In acquisition due diligence this is frequently the largest single risk and the least examined. Generate a full dependency licence inventory. Copyleft licences in a proprietary product, or a dependency whose licence changed after adoption, can be genuinely expensive to resolve.
The layer most reviews skip, and often the most predictive.
Where is the team cautious? Ask which part of the system they dislike changing. That answer identifies the real risk faster than any static analysis. Engineers know where the danger is; they are rarely asked directly.
Who is the only person who understands X? Key-person dependency is a top-three finding in most reviews and it is invisible in the code. Ask: if this person left tomorrow, what would take longest to recover?
What is the commit history shape? Long-lived branches suggest painful integration. A burst of commits at month-end suggests deadline-driven quality. One contributor across most of the codebase is a concentration risk.
What is in the issue tracker? Specifically the bugs that keep reopening. A defect fixed four times is a design problem wearing a bug's clothing.
What did the team want to do and not get to? Every team has a list. It is usually accurate and it usually maps directly to the real technical debt — more accurately than any external assessment will.
How is knowledge recorded? Architecture decision records, design docs, a wiki that is current. Or nothing, in which case the reasoning behind every non-obvious decision has already been lost, and future engineers will re-litigate settled questions.
Useful ones, and the ones to be sceptical of.
Metric | What it indicates | Concerning threshold |
|---|---|---|
Time to first local run | Overall maintenance health | Over 2 days |
Deploy frequency | Change confidence | Less than weekly |
Deploy duration | Batch size pressure | Over 30 minutes |
Change failure rate | Testing and review quality | Over 15% |
Time to restore service | Operational maturity | Over 4 hours |
Files touched per typical feature | Coupling | Consistently over 15 |
Dependency age (median) | Maintenance discipline | Over 2 years behind |
Bus factor on core modules | Key-person risk | 1 |
The first five are essentially the DORA metrics, and they are well-evidenced predictors of delivery performance. They are also easy to gather from CI and incident history without reading any code, which makes them ideal early in a time-boxed review.
Test coverage percentage. High coverage with poor assertions is common. Coverage of critical paths matters; the aggregate number does not.
Lines of code. Tells you nothing about quality or cost.
Cyclomatic complexity aggregates. Occasionally useful for finding specific hotspots, meaningless as a system-level score.
Static analysis issue count. Mostly measures whether anyone configured the linter.
The metrics that correlate with cost are about change behaviour, not code properties. That distinction is what separates a useful review from a generated report.
Ordered by how strongly each predicts expensive ownership.
Nobody can run it locally. No safe experimentation, no reliable estimates, and every change is a production test.
No backups, or never restored. An existential risk that is often assumed rather than verified.
Money as float in a financial system. Silent, retroactive, and every historical figure is suspect.
Authorisation enforced only in the frontend. Not a vulnerability to schedule — an open door.
Single contributor across the whole codebase, and they are leaving. The system's documentation is a person.
Copyleft dependency in a proprietary product. Potentially requires re-engineering or licence renegotiation.
No automated deployment, or no rollback that has ever been used
No observability beyond server logs
Direct cross-boundary database access
Deployed mobile clients against an unversioned API
Schema that cannot be migrated because no tooling exists
Dependencies more than three major versions behind
No staging environment
Secrets in git history
Low or absent test coverage on non-critical paths
Inconsistent code style
Documentation that is out of date but recoverable
Some technical debt the team can already name and locate
The distinction matters. Reviews that present everything at equal weight produce a hundred-item list nobody acts on. Six critical findings with costs attached get acted on.
The most consequential and most emotionally decided question in this space.
Rewrites usually cost two to three times the estimate and deliver no user value until they are finished. Meanwhile the existing system keeps changing, so the target moves throughout.
The industry evidence on large projects is discouraging enough — McKinsey and Oxford's study of 5,400+ large IT projects found average overruns of 45 percent, and software projects fared worst at 66 percent. A rewrite carries all of that risk with the additional handicap that the requirements are "everything the current system does," which nobody has actually written down.
The default answer should be extend. Rebuild needs to clear a high bar.
The data model cannot support the business direction. Single-tenant to multi-tenant, single-currency to international, single-country regulatory model to multi-jurisdiction. These are schema-level, and schema is the thing that does not refactor incrementally.
The platform is genuinely end-of-life. A framework with no security updates, a runtime nobody supports, a database version past extended support.
Nobody can run or change it. If the system is opaque and the original team is gone, you are not extending — you are reverse-engineering, and that can genuinely cost more than rebuilding.
The cost of ownership exceeds the cost of replacement. Rare, but calculable. If maintenance consumes six engineers to stand still, and a replacement is eighteen engineer-months, the arithmetic can favour replacement.
Strangler fig. Route traffic through a facade, replace one capability at a time behind it, delete the old code as each piece lands. The system works throughout, you can stop at any point and still be ahead, and you are never running a two-year project with no shippable output.
This is nearly always better than a rewrite and it is chosen less often because it is slower to start and less satisfying to plan. It is also the pattern that survives a change of priorities halfway through, which a big-bang rewrite does not.
Can you name the specific structural property that makes extending impossible?
If the answer is a concrete architectural fact — the schema assumes one tenant, the framework has no security patches — rebuild may be right. If the answer is that the code is messy, extend. Messy code is a refactoring problem, and refactoring is incremental in a way that rebuilding is not.
System size | Duration | Typical cost (India rates) |
|---|---|---|
Small — one application, under 50k LOC | 3 – 5 days | $2,000 – $4,000 |
Medium — several components, one team | 5 – 10 days | $4,000 – $9,000 |
Large — multiple services, several teams | 10 – 20 days | $9,000 – $20,000 |
Acquisition due diligence, time-boxed | 8 – 15 days | $8,000 – $16,000 |
Against a build that may cost $150,000 or more, or an acquisition in the millions, this is inexpensive insurance. It is also the cheapest way to discover that a vendor is wrong for you — a point we make in our engagement models guide, where discovery serves the same function.
Read access to repositories, CI history, incident history, monitoring dashboards, and the issue tracker. Interviews with two to four engineers. Ideally a demo environment.
If access is restricted, say what that limits. A review conducted on the code alone, with no CI history and no interviews, cannot assess operational maturity or key-person risk — which are two of the highest-value findings. State the limitation in the report rather than quietly reducing scope.
An executive summary a non-technical decision-maker can act on
Findings ranked by severity, with a clear critical tier
A costed remediation plan — each finding with an effort estimate
A sequence — what to do first, and what depends on what
An explicit extend-or-rebuild recommendation where that is the question
What was not assessed, and why
The test of a good review: could someone make a funding decision from the first two pages? If not, it is a technical document rather than a decision document, and it will sit unread.
The tool-generated report. A static analysis dump with no interpretation. Three hundred findings, no prioritisation, no cost. Impressive-looking and useless.
No cost attached. "The system has significant technical debt" is not actionable. "Six weeks to make deployment safe, three months to make the schema migratable" is.
Reviewing against an ideal rather than a purpose. Every system falls short of best practice. The question is whether it is fit for what the business needs next, not whether it matches a reference architecture.
Ignoring the team. Engineers know where the problems are. A review that does not interview them has discarded the cheapest and most accurate source available.
Recommending a rewrite by default. Sometimes an incoming vendor's incentive, sometimes genuine enthusiasm. Either way, treat a rewrite recommendation with scepticism unless a specific structural blocker is named.
Confusing unfamiliar with bad. A reviewer who does not know the framework will find it worse than it is. Unfamiliarity is not a finding.
No follow-up. A review with no owner and no sequenced plan changes nothing. Build the remediation into the next quarter's plan or do not commission it.
Print this. It is the practical output of the article.
Time for a new engineer to run it locally
README accurate as written
Dependencies pinned, audit run, median age noted
Realistic test data available
Local and production environment parity
Schema diagram obtained or generated
Primary key strategy, time storage, history, normalisation, money types checked
Migration history read
Migration tooling exists and works on large tables
Schema supports the business direction, not just its history
Two or three real recent changes traced through the code
God objects identified, inbound dependencies counted
Cross-boundary database access checked
Boundaries enforced in CI, not just documented
Any service genuinely deployable alone
Every external consumer enumerated
Mobile clients in the wild identified
Versioning approach and deprecation policy established
Contract specification exists and matches reality
Deploy frequency and duration measured
Rollback procedure exists and has been executed
Alerting exists and is acted upon
Backup restored, and restore time measured
Last incident and its remediation identified
Full git history scanned for secrets
Dependency vulnerabilities counted and aged
Authentication and authorisation implementation reviewed
Personal data locations mapped, including backups and third parties
Dependency licence inventory generated
Team asked what they avoid changing
Key-person dependencies named
Commit history shape reviewed
Recurring bugs identified
Team's own debt list obtained
A structured assessment of an existing system to determine what it will cost to own, extend and operate. It covers the data model, boundaries and coupling, external contracts, operational maturity, security posture and key-person risk. It differs from a code review, which evaluates code quality rather than change cost.
Three to five days for a single small application, five to ten for a medium system, and ten to twenty for multiple services across several teams. Acquisition due diligence is usually time-boxed to two weeks, which requires prioritising data model, security, licensing and key-person risk over everything else.
Typically $2,000 to $4,000 for a small system and $9,000 to $20,000 for a large multi-service platform at India rates. Against a build costing $150,000 or more, or an acquisition in the millions, it is inexpensive relative to the risk it retires.
Nobody can run it locally, backups have never been restored, money stored as a floating-point number in a financial system, authorisation enforced only in the frontend, a single contributor across the whole codebase who is leaving, and copyleft dependencies in a proprietary product. Each of these should trigger repricing rather than a remediation plan.
Extend by default. Rewrites typically cost two to three times the estimate and deliver no value until complete, while the existing system keeps changing. Rebuild only when you can name a specific structural blocker — a data model that cannot support the business direction, or a platform genuinely past end of life. Messy code is a refactoring problem, not a rebuild justification.
An architecture review conducted before an acquisition or investment, focused on whether the technology justifies the valuation and what liabilities it carries. Priorities differ from an ordinary review: licensing exposure, security posture, key-person dependency and whether claimed capabilities exist in the code matter more than code quality.
By measuring change cost rather than code properties. Trace two or three recent features through the codebase and observe how many files, modules and deployments each touched. Ask the team which parts they avoid changing. Aggregate metrics such as test coverage percentage and lines of code correlate poorly with the cost of ownership.
Time for a new engineer to run the system locally, deploy frequency and duration, change failure rate, time to restore service, files touched per typical feature, median dependency age, and bus factor on core modules. The first several are the DORA metrics and are well-evidenced predictors of delivery performance.
Partially. Code, schema and dependencies can be assessed from the repository alone, but operational maturity and key-person risk cannot — and those are among the highest-value findings. If interviews are unavailable, the report should state explicitly what could not be assessed rather than quietly narrowing scope.
An executive summary a non-technical decision-maker can act on, findings ranked by severity with a clear critical tier, a costed remediation plan with effort estimates, a recommended sequence, an explicit extend-or-rebuild recommendation where relevant, and a statement of what was not assessed. A review without cost estimates is a document, not a decision tool.
Prioritise operational readiness: can you build and run it, deploy it, roll back, and observe it in production. Then assess the data model and undocumented knowledge. The typical failure without this is quoting a feature roadmap against a system nobody has opened, then losing a quarter to stabilisation nobody budgeted for.
Less than commonly assumed. Coverage of critical paths matters; the aggregate percentage does not, since high coverage with weak assertions is common. Low coverage on core business logic is a serious finding. Low coverage overall, with the critical paths tested, is a notable one worth planning around rather than a blocker.
Inheriting a system is a pricing decision disguised as a technical one. The question is never whether the code is good — it is what this will cost to own, and where the surprises are.
The six things that predict that answer are unglamorous: can you run it, is the data model sound, is the coupling deliberate, what contracts constrain you, can you deploy and recover, and what does the team's own caution tell you. None requires sophisticated tooling. All require actually looking.
The most valuable question in any review is the simplest one: ask the engineers which part of the system they least like changing. They will tell you exactly where the money is going to go, and it takes ten minutes.
We run an architecture review before taking over any existing system, and we quote it separately from the work that follows. It typically costs a fraction of one percent of the eventual engagement and it is the reason our estimates on inherited systems hold up.
We have also walked away from engagements after a review, and told the client why. That costs revenue in the short term and it costs considerably less than committing to a roadmap against a system carrying a critical finding nobody had found yet.
Across 180+ software products delivered in 15+ industries, a meaningful share began as someone else's codebase. The pattern is consistent: the systems that were cheap to take over had boring, well-understood data models and a deployment pipeline that worked. The expensive ones had at least one critical finding from the list above, and in almost every case the team already knew about it — nobody had asked.
Our work with Patton Electronics began with exactly this kind of assessment, and it is what made the subsequent scope genuinely fixable rather than an estimate with a buffer.
We are rated 4.9 out of 5 from 110 Google reviews and 5.0 on GoodFirms.
If you are inheriting a system — from a vendor, an acquisition, or a team that has moved on — book a call with me directly. Bring whatever access you have. Even a repository and an hour with an engineer produces a useful first read.
You can also see how we structure custom software development engagements, enterprise application work and staff augmentation, or post your requirement for a response within one business day.
Subscribe to the Akoode newsletter for carefully curated insights on AI, digital intelligence, and real-world innovation. Just perspectives that help you think, plan, and build better.