Iurii RoguliaIurii Rogulia
AboutServicesPricingProjectsStackReviewsPhrasesBlog
Contact
Iuriiย ships.

Iurii Rogulia, IT partner for business & fractional CTO. Professionally building software since 2001.

Think of a number
PricingQuality checklistPrivacy PolicyCookie Policy

Business

TMI Iurii Rogulia
VAT ID: FI29845875
DUNS: 368664211
Lappeenranta, Finland ๐Ÿ‡ซ๐Ÿ‡ฎ

[email protected]
  1. Home
  2. /
  3. Blog
  4. /
  5. How to Take Over a Codebase from a Web Agency (5-Day Process)

Iuriiย Audits: How to Take Over a Codebase from a Web Agency (5-Day Process)

What I check in the first 48 hours when inheriting a production app from an agency โ€” and why reading comes before fixing

April 27, 2026ยท 12 min read

Agency codebase handover checklist: first 48 hours, security triage, architecture mapping, and how to write a takeover assessment. Practical guide.

Stack

TypeScriptNode.js

Topics

ArchitectureTechnical DebtSecurityProcess
How to Take Over a Codebase from a Web Agency (5-Day Process)

On this page

  • The First Rule: Read Before You Touch Anything
  • Set Up the Local Environment โ€” and Document Every Obstacle
  • Map the Architecture from the Code, Not the README
  • Security: Look Here First
  • Tests: Do They Exist, and Do They Actually Pass?
  • Identifying Real Debt vs. the Appearance of Debt
  • Common Agency Code Smells Worth Documenting
  • Write the Takeover Assessment
  • Stabilize First. Almost Never Rewrite.
  • When You've Done This

The project has been delivered. The agency has invoiced. The client now has a repository, a deployed app, and a README that says "see docs/setup.md" โ€” which doesn't exist.

This is when they call me.

I've taken over enough agency codebases to have a process. Not a checklist you run through once and declare victory, but a way of thinking about an unfamiliar system that's already in production. The goal for the first few days is not to fix anything. It's to understand what you actually have โ€” which is almost always different from what was documented or promised.

The First Rule: Read Before You Touch Anything

The instinct, especially if the code is visibly bad, is to start cleaning. Don't.

Until you understand the system, you don't know which parts are load-bearing. I've seen codebases where a function named doStuffOld turned out to be the function that actually ran in production because someone had quietly edited a config file to point at it after the "new" version broke something. If I'd deleted it based on the name alone, the app would have failed silently.

The first 48 hours are for reading: the repository, the deployment setup, the issue tracker if there is one, and any communication history you can access. You're building a mental model before you touch a single file.

One thing I check in the first hours that's easy to overlook: observability. Does error tracking exist? Are there logs somewhere, and can you actually query them? Are there uptime alerts, and do they go to someone who can act on them? The practical question is simple: if the system goes down at 2am, who finds out, and how? Agency projects frequently have no answer to this. That's not a code quality problem โ€” it's an operational risk that belongs at the top of your assessment.

Set Up the Local Environment โ€” and Document Every Obstacle

Getting the project running locally is the first real test. Do it from scratch: fresh clone, follow the README exactly, and write down every step that's missing, wrong, or requires tribal knowledge.

The list of obstacles you collect here becomes part of your takeover assessment. It also tells you something about code quality in general โ€” a project whose local setup requires six undocumented environment variables and a Postgres version that's three major releases behind has almost certainly accumulated similar debt everywhere else.

Common things I find:

  • .env.example that's either empty or contains production values from someone's machine
  • Node version not specified anywhere (and the app breaks silently on newer versions)
  • Missing setup steps for third-party services โ€” "you'll need a Stripe account" but no indication which keys go where, or how to configure webhooks for local development
  • Database migrations that don't run cleanly, or no migrations at all โ€” just a SQL dump from a specific point in time

If you can't get it running locally, that's the first item in your assessment. The client often doesn't know this is a problem because the agency handed over a deployed URL, not a functional development workflow.

Map the Architecture from the Code, Not the README

If there's architectural documentation, read it โ€” then set it aside and look at the actual code. The two rarely match, and the delta between them is where problems hide.

What I'm trying to understand:

What does the data flow look like? Where does a request enter the system, what touches it, what gets written to the database. I trace a few representative paths โ€” a user creation, an order, a webhook โ€” manually through the code. This is tedious but irreplaceable.

What are the actual dependencies? Open package.json. Not the categories, the specific packages and their versions. Agency projects frequently have dependencies that are two major versions behind, packages that have been abandoned, or three different date-formatting libraries installed simultaneously because different developers had different preferences.

What connects to what externally? Third-party API calls, webhooks incoming and outgoing, cron jobs, queues. These are the integration points where failures are hardest to debug and where undocumented behaviour lives. Map them before anything else.

Where is state kept? Database, Redis, local memory, cookies, environment variables used as runtime config. If there's state living somewhere it shouldn't โ€” like feature flags hardcoded in environment variables that can't be changed without redeployment โ€” that's worth flagging.

Security: Look Here First

Before I think about performance or clean code, I look for security problems. These are the issues where "we'll fix it later" means "we'll fix it after the breach."

Related service

Rescue Projects

Inherited an agency codebase and not sure what you're actually dealing with? I do this assessment professionally โ€” code audit, risk triage, and a clear path forward.

More about this service โ†’

The things I check immediately:

Secrets in the repository. Run a search for anything that looks like a key or token: grep -r "sk_live\|pk_live\|AIza\|AKIA" --include="*.js" --include="*.ts" . โ€” and check the git history too, because secrets removed from HEAD are still in the history. If production secrets have ever been committed, that's a hard stop โ€” rotate them before anything else.

No authentication middleware on protected routes. Agency code often handles auth at the individual endpoint level, which means it's one missed check away from a gap. Look for a consistent auth layer, not scattered if (!user) return 401 calls.

SQL injection surface. If the project uses raw queries rather than an ORM, search for string interpolation in query construction. Even ORMs can be misused โ€” look for db.raw() calls with user-supplied data.

Missing rate limiting. Login endpoints, password resets, API routes that do expensive operations โ€” check whether any rate limiting exists. If it doesn't, add it to the assessment as a risk, not as a task for this week.

CORS configuration. Access-Control-Allow-Origin: * on an API that handles authenticated requests is worth flagging even if it's not actively exploitable today.

Dependency vulnerabilities. Run npm audit (or the equivalent for your package manager) and look at what comes back. Agency projects often arrive with dependencies that haven't been reviewed in a while. Critical or high-severity CVEs in production dependencies belong in the immediate-action tier, not the backlog.

I don't fix all of these immediately. I triage: what needs to happen today, what needs to happen this week, what goes in the medium-term backlog.

Tests: Do They Exist, and Do They Actually Pass?

Run the test suite if there is one. Then read the tests.

Agency projects frequently have tests that were written to satisfy a checklist, not to catch bugs. The signs: tests that only assert that a function returns without throwing; tests that mock every dependency to the point where they can't catch any integration issue; coverage numbers that look impressive because the same happy-path code is tested fifteen different ways while error handling is untested entirely.

Sometimes there are no tests at all. This is actually easier to work with than the false confidence of a suite that passes but doesn't cover anything meaningful.

What the test suite tells you: where the previous developers thought the risky parts were. Even bad tests are a form of documentation about intent.

Whatever you inherit, don't start by rewriting the tests. Start with characterization tests โ€” tests that document what the code currently does, not what it should do. Run a critical path, capture the output, write a test that asserts it. This gives you a safety net before you touch anything, and it forces you to actually understand the behaviour rather than assume it. Once you have that baseline, you can add meaningful coverage for the paths that matter most to the business.

Identifying Real Debt vs. the Appearance of Debt

Not all messy code is actually broken. This distinction matters because it affects where you spend time.

Appearance of debt โ€” things that look bad but work fine:

  • Inconsistent naming conventions across files (one developer used camelCase, another preferred snake_case in the same object)
  • Functions that are longer than you'd write them but do exactly one clear thing
  • Comments that are out of date but harmless
  • Code that could be DRY but is repeated in a way that's easy to follow

Real debt โ€” things that will cause failures:

  • Error handling that consists entirely of catch (e) {} โ€” exceptions swallowed silently
  • No retry logic on external API calls that will occasionally fail
  • Copy-pasted route handlers where a bug fixed in one was never fixed in the others
  • Database queries inside loops (N+1 problems that are fine at current scale and catastrophic at 10x)
  • No database migrations โ€” state is only defined by the current schema, with no record of how it got there

The practical test: does this cause a bug today, will it cause a bug under more load, or is it just aesthetically unpleasant? Prioritize the first two. Don't spend the first sprint making things prettier.

Common Agency Code Smells Worth Documenting

These aren't dealbreakers, but they're worth naming in your assessment so the client understands what they inherited:

No error handling on async operations. A fetch call or database query that throws will surface as an unhelpful 500 with no context. In Node.js this can silently swallow errors in some contexts.

Copy-pasted route handlers. Instead of a shared function, the same 40 lines of logic duplicated across three routes with minor variations. Bugs fixed in one are silently present in the others.

Hardcoded config values. Database connection strings, API base URLs, or timeouts hardcoded in the middle of a function rather than environment variables or config files. Finding and changing them later is a manual search.

No database migration history. The database schema exists, but there's no record of changes over time. You can't safely run a fresh setup or roll back. Schema and application code are permanently coupled in an opaque way. This is one of the patterns I cover in detail in PostgreSQL production patterns โ€” schema drift deserves its own section.

Deployment that only works from one person's machine. The CI/CD pipeline exists but requires manually uploaded files, environment variables set through a web UI with no documentation, or build steps that assume a specific OS path structure.

Write the Takeover Assessment

Before you start changing anything, produce a written document. This protects you and clarifies your thinking.

The assessment I write covers:

  1. What I found โ€” architecture overview based on what's actually in the code, not what was described
  2. What runs locally and what doesn't โ€” including every undocumented step I had to figure out
  3. Security issues โ€” triaged by severity: immediate action, this sprint, backlog
  4. Actual vs. claimed functionality โ€” if the spec said feature X was implemented and it isn't, or it's implemented partially, name it explicitly
  5. Risks โ€” technical debt items that are not bugs today but will become bugs under specific conditions. For each one, frame it as probability ร— impact: "this will fail when concurrent users exceed X, and when it does, it takes the entire checkout flow down" is something a client can act on. "this code is not ideal" is not.
  6. Recommended path forward โ€” stabilize first, then improve

The audience for this document is the client, not a developer. They need to understand what they're working with in terms of business risk, not code quality scores.

Stabilize First. Almost Never Rewrite.

The advice I give every time, even when the code is genuinely bad: don't rewrite from scratch. Before you commit to a rewrite at all, the right first step is a structured technical due diligence engagement โ€” not jumping straight to rescue-project mode.

I understand the impulse. The codebase is a mess, it's faster to start clean, you won't carry forward the old decisions. Every developer who's ever inherited a bad codebase has felt this. It is almost always the wrong call.

Rewrites fail for a consistent set of reasons. The old code, however ugly, contains implicit knowledge about edge cases โ€” validation rules, data inconsistencies in the database, integrations that behave unexpectedly under specific conditions. That knowledge took years and customer incidents to accumulate. A rewrite starts without it and rediscovers all of it the hard way. Meanwhile, the business is running on the old system and nothing new has shipped.

The better path: stabilize what exists, fix the actual security issues and bugs, and then incrementally improve. Replace the most problematic modules one at a time, with tests before and after โ€” the strangler fig pattern works well here: build the replacement alongside the old code, route traffic to it gradually, then delete the original once you're confident. This is slower and less satisfying but it produces working software rather than a new codebase that will need its own takeover in two years.

The exception is when the architecture is fundamentally incompatible with the business requirements โ€” not messy, but structurally broken in a way that makes adding new features nearly impossible without touching everything. That's rare. More often, "this needs a rewrite" is an aesthetic judgment dressed up as an engineering one.

When You've Done This

You'll have: a running local environment with setup fully documented, an architecture map based on actual code, a triaged security issue list, an honest assessment of what works and what doesn't, and a clear recommendation for next steps.

This takes three to five days for a medium-sized codebase. It's not glamorous work. But it's the difference between taking over a project and being taken over by it.


If you've inherited an agency codebase and aren't sure what you're actually dealing with, or if you need someone to do this assessment and tell you honestly what the path forward looks like โ€” get in touch. I've done this across multiple production systems, including pikkuna.fi, and I know what to look for.

Iurii Rogulia

Working on something like this?

Rescue Projects

Inherited an agency codebase and not sure what you're actually dealing with? I do this assessment professionally โ€” code audit, risk triage, and a clear path forward.

More about this service

Relevant client work

View all projects
n8n Community Node โ€” EU VAT Validation in Workflows
n8n Community Node โ€” EU VAT Validation in Workflows
August 6, 2026
n8n Community Node โ€” EU VAT Validation in Workflows

Community node that puts VIES VAT validation and EU rate lookups inside n8n, so an onboarding or invoicing workflow can decide on a real VAT number instead of

pi-pi.ee โ€” B2B Deal & Document Portal
pi-pi.ee โ€” B2B Deal & Document Portal
July 1, 2026
pi-pi.ee โ€” B2B Deal & Document Portal

Internal sales portal that turns a wholesale deal into a full set of trade paperwork โ€” pro forma, contract, commercial invoice, packing list, CMR and more โ€”

vatnode-mcp โ€” Official MCP Server for EU VAT Validation
vatnode-mcp โ€” Official MCP Server for EU VAT Validation
May 20, 2026
vatnode-mcp โ€” Official MCP Server for EU VAT Validation

Open-source MCP server that lets Claude Desktop, Cursor and other MCP clients validate EU VAT numbers and look up rates directly in chat.

What clients say

โ€œ

I'd built most of our MVP with Cursor and it looked finished โ€” it compiled, the tests were green, the demo worked. It just wouldn't survive real users. Iurii took it over and found what I couldn't see: authentication handled four different ways, tests that only asserted what the code already did, and a dependency list half of which was unused. He didn't rewrite it from scratch โ€” he told me honestly what was salvageable, ripped out the dead code, and got it to something a real team could build on. Two weeks and it went from 'looks done' to actually shippable.

Sebastian Falk ๐Ÿ‡ธ๐Ÿ‡ช

Founder

Stack

Next.jsTypeScript

Topics

Technical DebtAICode ReviewArchitecture
โ€œ

We took over a Next.js project from another agency and inherited a mess โ€” failing builds, no tests, deployments that worked locally and broke in production. Iurii spent two weeks getting it stable before touching any new features. He documented what was broken, fixed the critical issues, and left us a short list of the smaller things we could handle ourselves. By the end the project was actually maintainable, which had felt impossible at the start.

Niamh O'Sullivan ๐Ÿ‡ฎ๐Ÿ‡ช

Agency Owner

Topics

Technical DebtArchitectureProcess
โ€œ

We had an acquisition offer on the table and I needed to know whether our own codebase would survive the buyer's technical review before I walked into that room. Iurii did a five-day independent audit and gave me a written report I could actually act on โ€” what was solid, the two things that would raise flags in a data room, and what it would take to fix them first. He caught an undocumented dependency on one ex-contractor's private package that would have been a red flag the moment their team looked. We cleaned it up before diligence started, and the deal closed without a single technical concession. Best money I spent on the whole process.

Wouter Claes ๐Ÿ‡ง๐Ÿ‡ช

Founder & CEO

Topics

Due DiligenceArchitectureCode ReviewTechnical Debt

Related articles

Multi-Tenant SaaS Database Design: Shared Schema vs Schema-per-Tenant
June 19, 2026ยท 18 min
Multi-Tenant SaaS Database Design: Shared Schema vs Schema-per-Tenant

Multi-tenant SaaS database design: shared schema with RLS, schema-per-tenant, and database-per-tenant โ€” trade-offs, real pitfalls, and when to use each.

Stack

TypeScriptNode.js

Libraries

Drizzle ORM

Databases

PostgreSQL

Topics

SaaSArchitectureSecurity
JWT vs Sessions vs OAuth: Which to Use for SaaS Auth
May 14, 2026ยท 15 min
JWT vs Sessions vs OAuth: Which to Use for SaaS Auth

JWT vs sessions vs OAuth for SaaS: token invalidation, mobile clients, refresh token rotation, and a decision matrix to pick the right auth pattern.

Stack

Next.jsTypeScriptNode.js

Libraries

Better Auth

Databases

PostgreSQLRedis

Topics

SaaSAuthArchitectureSecurity
API Key Management for a Public SaaS API
August 5, 2026ยท 13 min
API Key Management for a Public SaaS API

API key management for a public SaaS: hashing keys at rest, prefix + last-4 display, fail-closed validation, and revocation โ€” plus the scoping, rotation, and

Stack

TypeScriptNode.jsHono

Libraries

Drizzle ORM

Databases

PostgreSQL

Topics

SaaSAPIAuthSecurity
Preventing Overselling: Inventory Locks Under Concurrent Checkouts
July 31, 2026ยท 13 min
Preventing Overselling: Inventory Locks Under Concurrent Checkouts

Prevent overselling under concurrent checkouts: reservations vs hard decrements, SELECT FOR UPDATE, deadlock-safe multi-line carts, and the payment window.

Stack

Next.jsTypeScriptNode.js

Databases

PostgreSQLRedis

Topics

E-commercePaymentsArchitectureSaaS
Gated B2B Pricing in Next.js: Hiding Prices Behind Per-Customer Access
July 24, 2026ยท 9 min
Gated B2B Pricing in Next.js: Hiding Prices Behind Per-Customer Access

Gated B2B pricing in Next.js: per-account price lists, server-side access control so prices never leak to crawlers, and a deal-record data model where every

Stack

Next.jsReactTypeScript

Databases

PostgreSQL

Topics

B2BE-commerceAuthSSRArchitectureSecurity