TL;DR: When you inherit a codebase someone else built, do not start by reading code and do not start by rewriting anything. Spend the first week answering five questions in order: can I build and ship it myself, where does the truth live, what do real users touch most, what breaks silently, and who holds the knowledge that is not written down. Only after that do you have the right to decide what to fix, what to leave alone and what to rebuild. I have walked into live products more than once, most visibly stepping in as CTO at RAQTS during a critical phase across Unity, React Native and Next.js, and giving CTO-level guidance on an existing React, TypeScript and GraphQL app for Amazon sellers at Bettershop. The checklist below is what I actually do in that first week.
Founders usually call me at one of two moments. Either the original developer or agency has left and nobody fully understands the system anymore, or the product is live, growing, and every release feels like it might break something. In both cases the founder wants to know the same thing: is this codebase salvageable, and what should we do first?
The honest answer is that you cannot know from reading the code. You know from running it, shipping it and watching where it hurts. That is what the first week is for.
Rule zero: do not rewrite anything in week one
Every engineer who inherits a codebase feels the pull to rewrite it. The code looks unfamiliar, the conventions are not yours, and some of it is genuinely bad. But unfamiliar is not the same as wrong, and a rewrite started before you understand why the code looks the way it does will faithfully reproduce the bugs the original team already fixed, minus the fixes.
When I came into RAQTS, the platform was already live with players and facilities using it. The first job was not to make it beautiful. It was to map what already worked well enough not to break it, which I wrote about in stepping in as CTO mid-build. You earn the right to change a system by understanding it first.
So the first week is read-only in spirit, even if you fix a typo or two. Here is the order I work in.
1. Can I build and ship it myself?
Before anything else, I try to go from a clean machine to a running build, and then from a code change to something a real user could receive. This sounds basic. It is the single most revealing test of an inherited project.
What I am checking:
- Can I clone it and run it with only what is in the repository and the documentation? Every undocumented step, a secret someone has on their laptop, a manual database seed, a specific tool version, gets written down as I hit it.
- Who owns the accounts? App Store Connect, Google Play Console, the cloud provider, the domain, the DNS, the payment provider, the analytics. On a product with mobile apps, losing access to the store accounts is far worse than any code problem. I want the company, not a departed contractor, holding the keys.
- Can I produce a release? For the web, can I deploy. For mobile, can I produce a signed build, and do we have the signing keys and certificates. For a Unity project, does the build pipeline actually exist or does it live in one person's editor. I have written about why a real Unity CI/CD pipeline matters, and an inherited project is exactly where its absence bites.
If I cannot ship it, nothing else matters yet. A product you cannot release is a product you cannot fix.
2. Where does the truth live?
Every product has a handful of facts it cannot afford to get wrong. For a sports platform it is a player's score and position on a leaderboard. For an analytics app for Amazon sellers it is the profit figure, which means how fees and returns are modelled. For a voice agent business it is the lead record in the CRM.
For each of those, I trace where the authoritative version lives and how many places compute or store a copy. The most common serious problem I find in inherited products is not ugly code. It is the same fact being calculated in two or three places that have quietly drifted apart: the mobile app shows one number, the web portal another, and the backend a third.
On RAQTS the fix direction was to make the backend the single source of truth for live leaderboards, with the Unity Lumo layer, the mobile apps and the web portal all rendering it rather than deciding it. On the Bettershop dashboard the expensive decision was the data model behind profit, which I covered in building a GraphQL admin dashboard for Amazon sellers. In both cases, getting the truth in one place mattered more than any refactor of the UI code around it.
By the end of this step I can usually draw the product's data flow on one page. If I cannot, that itself is the finding.
3. What do real users actually touch?
Not every part of a codebase deserves the same attention. A messy admin settings screen that one person opens once a month is not a priority. A messy checkout, sign-up or booking flow that every user passes through is.
I look at analytics if they exist, support messages, store reviews and, where I can, I just use the product the way a customer would. The goal is a short list of the three to five paths that carry the business. Those are where I want the code to be boringly reliable, and where any refactoring budget should go first.
This is also where I find out whether the product has analytics worth trusting at all. Inherited products often track events that were named by three different people in three different styles, and some of them fire twice. Knowing that early stops you from making decisions off broken numbers.
4. What breaks silently?
Loud failures get fixed because somebody complains. Silent failures sit for months. In an inherited system I specifically hunt for the places where something can go wrong and nobody would notice:
- Background jobs and scheduled tasks. Crons, queues, webhooks, sync jobs. Are they monitored? If one stopped running last Tuesday, how would anyone know?
- Integrations with third parties. Payment webhooks, CRM syncs, email and SMS providers. These fail at the boundary, and the failure is often swallowed by a catch block that logs to nowhere. I wrote about this pattern in voice agent systems in why post-call automations fail quietly, and it is identical in any product.
- Old app versions. On mobile, users do not all update. If the backend changes shape, the version from eight months ago may be breaking for a slice of users who never report it. That is the subject of keeping old mobile app versions working.
- Error reporting. Is there crash reporting on mobile and error tracking on the backend? Is anyone reading it? A dashboard full of ignored errors is a backlog nobody has prioritised.
Silent failures are where the real risk in an inherited codebase usually lives, so they get the most time.
5. Who knows what is not written down?
Every system has knowledge that only exists in someone's head: why a strange workaround exists, which customer has a special configuration, which setting must never be changed. If the original developer is still reachable, even for a paid hour or two, that conversation is the most valuable hour of the week. I go in with a list of the oddest things I found and ask why each one is there.
If they are not reachable, the git history, old tickets and old chat threads are the next best thing. A commit message from two years ago explaining a hack is worth more than the hack itself.
I write all of it down in the repository, not in my notes. The point of the exercise is that the next person, including me in three months, does not have to rediscover it.
What comes out of the week
At the end of the first week I give the founder a short written assessment, not a slide deck. It usually has four parts:
- Keep as is. The parts that work well enough and do not justify the risk of change. This list is usually longer than founders expect, and that is good news.
- Fix now. Account ownership, release blockers, silent failures with real business impact, and any place where the truth is computed in more than one location.
- Improve as you go. Code quality problems in the high-traffic paths, cleaned up alongside feature work rather than in a separate project.
- Rebuild, maybe. The rare part where the cost of keeping it is clearly higher than replacing it, with an honest estimate. Most of the time this list is empty or has one item.
That structure keeps the conversation away from "is the code good or bad", which is not a useful question, and toward "what do we do on Monday", which is.
Why this works across very different stacks
I have done this on a sports-tech platform spanning Unity, React Native and Next.js, on a React and GraphQL analytics app, on cross-platform SDK work at Geonode, and on voice agent systems built by other people. The stacks have almost nothing in common. The questions are identical.
That is the same reason I can hold several fractional CTO roles without relearning everything each time. The durable skill is not knowing a particular framework. It is knowing what to look at first, in what order, and having the discipline not to touch anything until you understand why it is the way it is.
If you have just inherited a product, or you are a founder whose developer has moved on, resist the urge to rewrite and resist the urge to panic. Spend a week finding out what you actually have. It is almost always more salvageable than it looks on day one.
I step into existing products as a fractional CTO and engineer, across web, mobile, Unity, XR and AI agent systems, for founders in the US, UK and Europe. If you have inherited a codebase and want a clear read on what to keep, fix and rebuild, more about my background is here, or book a call.