An AI-built product can look healthy right up to the moment it stops being easy to change. Features ship quickly, tests pass, customers sign up. Then a small change takes a week, or a bigger customer breaks something that worked for everyone else.
You can see most of that coming. Architecture problems leave marks in your repository, and you can measure them in an afternoon with a few commands. This is how I evaluate a codebase before a team puts more users, more money or more engineers on top of it.
Step 1: Measure the structure, not your impression of it
Oversized files
Files that grow past about 1,000 lines almost always mix several jobs.
git ls-files | grep -E "\.(ts|tsx|js|py)$" | xargs wc -l | sort -rn | head -15
The file length alone does not decide anything. What matters is the longest function inside it. In one audit, a single function in a worker file ran for about 1,500 lines and sat on the path that handled paid runs. See the architecture example report.
Change hotspots
The files that change most are where your risk is. A big file that nobody touches is not urgent. A big file that changes every week is.
git log --since="6 months ago" --name-only --pretty=format: | sort | uniq -c | sort -rn | head -20
Files that change together
If two files change in the same commit again and again, they are one unit that happens to be split in two. The next person will forget one of them. Tools like git log --name-only and a short script will show the pairs. In the audit above, six pairs changed together between 9 and 15 times in 500 commits.
Who knows this code
git shortlog -sn --since="6 months ago"
If one author identity wrote almost everything, you have a bus factor of one. That is not a flaw to fix with code. It is a risk you should know about and plan around, for example by writing down how to run, test and deploy.
Step 2: Check the guardrails
- Tests: count test files against source files, then check which flows they cover. In the same audit there were 207 test files for 512 source files, which is a good ratio. The question that matters is whether the money path is in them.
- Circular dependencies:
npx madge --circular --extensions ts,tsx srclists them. They make code hard to split later. - Layering: database calls inside UI components, and
fetchcalls scattered across pages, mean any change to the data layer touches many files. - Duplicated logic: the same long line in 12 or 18 files is copy-paste. If it is authentication or error handling, a missed copy is a bug on a sensitive path.
- Dependency drift: run
npm outdated. Being a major version behind on several packages means upgrades will arrive all at once, usually when a security advisory forces them.
Step 3: Ask what breaks first when you scale
Scaling usually fails somewhere specific, and it is rarely the number of users. It is often the size of one customer's data.
- Unbounded reads. Reads with no limit return everything, or silently stop at a cap. Supabase returns at most 1,000 rows by default without an error. Search for reads with no
.range()or.limit(). - One query per item. A database call inside a loop is fine for 5 items and a problem for 5,000.
- State in memory. A rate limiter or cache stored in one server's memory behaves differently the moment you run two servers.
- Calls without a timeout. A slow dependency then holds requests open and keeps you paying for idle capacity.
- Cold starts. If the app scales to zero, measure the first request after a quiet period.
Do not guess how much traffic you will have. Write down what you actually know: peak requests, the size of your largest customer, the slowest page. If you do not know, that is a finding in itself. The scalability example report has a table of exactly this: the bottleneck, where you are now, the limit, and when you will reach it.
Step 4: Turn findings into a plan, not a rewrite
The goal is not a rewrite. For each problem, ask what it costs you today and whether it gets worse with every feature. Then rank:
- Anything that can lose or silently corrupt data. Fix first.
- Hotspots that change weekly. Add tests around them, then split one piece at a time without changing behaviour.
- Copy-pasted sensitive logic. Move it into one helper and adopt it route by route.
- Everything else, when you next touch the area.
Small steps matter because the code works. You want each change to be one commit that you can revert.
What this does not cover
Measurements show where the structure is weak. They do not show whether the product is built on the right idea, or whether your team can maintain it. Pair them with a conversation about what is most painful to change today and what the next twelve months look like.
Do it with your own AI, or ask me
The architecture and scalability audits I run follow these steps, with a scanner that measures the structure and a manual checklist on top. You can run them yourself with your own AI assistant, or I can go through the system and write the report. Read a real architecture report first, then look at pricing.