Skip to main content
Engineering

The First Two Weeks Inside Someone Else's Codebase: Our Day-by-Day Read Order, Instrumentation, and the 6 Findings That Reset the Quote

What an incoming engineering team actually does between saying yes and owning the repo, logged day by day across a payments platform, a multi-sided marketplace, and an aging PHP monolith.

2026-09-18 · By Filip Lauc

Why day one starts with the deploy pipeline, not the source code

On day one we read the deploy pipeline and environment configuration, not application code. CI config, secrets management, environment variable lists, and the hosting console tell you how many environments exist, what actually runs in production, and which repositories are live. Source code tells you none of that.

Reading source first is the most common mistake incoming teams make. You spend three days forming a mental model of a service that turns out to be deployed from a branch nobody merges, or that was replaced by a serverless function six months ago and left in the repo. The pipeline is the only artifact that cannot lie about what is running. If the deploy step pushes to a host you were not told about, that is a system you inherited without knowing it.

Concretely, on one PHP monolith we took over, the CI file referenced three deploy targets: production, a staging box, and something called staging-2. Nobody on the client side knew what staging-2 was. We came back to it on day four. That single file was worth more than the first week of reading controllers.

  • CI/CD config files: every deploy target, every branch trigger, every manual gate
  • Environment variable inventory per environment, with the values redacted but the keys visible
  • Hosting and cloud console: running services, scheduled jobs, queues, storage buckets, load balancers
  • DNS records and certificate expiry dates, including subdomains nobody mentioned
  • Third-party dashboards: payment provider, email sender, error tracker, analytics, feature flags

The questions we ask the outgoing team before their access is revoked

The outgoing team's knowledge has a hard expiry date, usually the last day of their contract. We book two structured sessions in week one and ask questions that only a human who lived in the system can answer: what breaks at 3am, what nobody is allowed to touch, and which manual step is not in any runbook. Architecture can be reconstructed later. Tribal knowledge cannot.

We keep the tone collaborative, because the outgoing developers are often the only reason the thing still works, and they know it. We do not ask them to justify decisions. We ask them what they would fix first if they had two weeks, which is the same question we are answering, and their answer is usually right. When we are joining an existing engineering team rather than replacing one, the same questions apply, just spread over coffee rather than a single handover call.

We record the sessions with permission and transcribe them. Six weeks later, when a cron job fails at midnight, the sentence you need is thirty-eight minutes into a call you half-remember.

  • What runs on a schedule, where is it configured, and what happens if it does not run?
  • Which part of the system has no test coverage and you would not change without a manual check?
  • Is there any manual step in a release, a month-end, or an onboarding that is not written down?
  • Who else has production access, including contractors, agencies, and the founder's personal account?
  • What was the last serious incident, what caused it, and what did you change afterwards?
  • What did you always want to fix and never got budget for?

Instrumentation before modification: finding the undocumented cron jobs and webhook consumers

In week one we add observability, not fixes. Request logging at the edge, a log line at the entry of every scheduled task, inbound webhook logging with full headers, and outbound HTTP call logging. Two to three days of traffic reveals the jobs, integrations, and consumers that no document mentions and no one remembers configuring.

The highest-yield instrumentation is the cheapest: log every inbound request that is not from a browser session. On one marketplace takeover, that surfaced a partner pulling a CSV export endpoint every morning at 06:00 with a token issued years earlier. Nobody in the current business knew the integration existed. Turning it off would have broken a relationship; leaving it undiscovered would have meant breaking it accidentally during a refactor.

Outbound logging matters just as much. Every external call is a dependency you are about to own, including the ones with no line item in the client's vendor list. We tally them into an integration inventory with owner, credential location, renewal date, and blast radius if the call starts failing. On the PHP monolith the inventory came out four entries longer than the client's own list of integrations, one of which was an SMS gateway with a prepaid balance nobody was monitoring.

Reconstructing the domain model from the database when there are no docs

When no documentation exists, the database schema is the most honest specification available. We dump the schema, draw the foreign keys, then run counts and distinct-value queries on every status, type, and flag column. The rows tell you which states are actually reachable and which parts of the model were abandoned.

The method is mechanical. First, list every table with row counts and the date of the most recent insert. Tables with zero writes in a year are dead or nearly dead, and you should say so out loud before anyone asks you to maintain them. Second, for each enum-like column, get the distribution. A status column with seven documented values and three in use tells you the workflow was simplified and the code still branches on states that never occur. Third, look for the columns that encode business rules: nullable foreign keys, soft-delete flags, and anything named legacy_, old_, or tmp_.

Then we validate the reconstruction against reality by sitting with an operations person for an hour and walking one real record end to end. On a logistics-heavy marketplace, that meant following a single order from the consumer app through the supplier portal, batching, driver assignment, and invoicing. The walkthrough contradicted our schema reading twice, and both contradictions were business rules living in application code rather than in the data model. That hour is non-negotiable.

The six findings that changed the maintenance quote

Six categories of finding have repeatedly moved our maintenance price or scope: a payment write path with no idempotency, staging environments pointing at production data, credentials with no rotation path, scheduled jobs with no failure alerting, a build that cannot be reproduced locally, and code paths whose behaviour depends on data nobody can regenerate. Each one converts directly into engineer-days.

The payment idempotency finding is the one we treat as a hard gate. If a retry, a double-submitted form, or a webhook redelivery can create a second charge or a second order, the cost of owning that system is not the maintenance retainer, it is the incident. We price the fix, an idempotency key on the write path plus a dedupe table, as a precondition rather than a backlog item. The staging finding from day one turned out to be exactly that flavour: staging-2 shared the production database credentials, meaning any developer testing a destructive migration was one environment variable away from a bad afternoon.

We write every finding in the same shape so the client can price it rather than argue about it: what it is, how we found it, what happens if nothing changes, the engineer-day estimate to fix, and whether it is a precondition of us taking over or something we will carry.

  • Non-idempotent write path on money or inventory: retries and webhook redeliveries can duplicate state
  • Environment bleed: a non-production environment reading or writing production data
  • Credentials with no rotation path: shared accounts, keys in the repo history, no secret manager
  • Silent scheduled jobs: crons with no success signal, so failure is only visible downstream weeks later
  • Non-reproducible build: production works, a fresh clone does not, because of undocumented manual setup
  • Irreplaceable data coupling: logic that depends on hand-edited rows or a one-off import nobody can repeat

The two-week deliverable: system map, risk register, and the refusal list

We end the two weeks with three documents: a system map of every deployed component and integration, a risk register where each item carries an engineer-day cost and an owner, and an explicit list of things we will not take responsibility for until they are fixed. The third document is the one that protects both sides.

The system map is deliberately boring. One page per environment, boxes for deployed services, arrows for data flow, and a footnote for every credential and its location. If a new engineer cannot use it to answer "where does an order actually get written" in under a minute, it is too clever. The risk register is sorted by expected cost, not severity label, so a medium-likelihood data-loss risk outranks a high-severity theoretical one.

The refusal list is short and specific. Not "we will not own bad code", but "we will not accept on-call responsibility for the payments service until idempotency keys are on the charge endpoint, and we will not run migrations while staging-2 shares production credentials". Stating it in writing before the maintenance contract starts turns a future argument into a scheduled piece of work. In practice every client we have handed this to has funded the fixes, because the register prices them and the alternative is paying for the same problems as incidents instead.

Key Takeaways

  • Read the deploy pipeline, environment config, and hosting console before any application source: they are the only honest record of what is actually running.
  • Interview the outgoing team on a deadline. Tribal knowledge about crons, manual steps, and past incidents disappears the day their access is revoked.
  • Add logging on inbound webhooks, scheduled jobs, and outbound HTTP calls in week one. Two or three days of traffic surfaces integrations nobody documented.
  • Reconstruct the domain model from schema plus row counts and enum distributions, then validate it by walking one real record end to end with an operations person.
  • Ship three artifacts at day ten: a system map, a risk register priced in engineer-days, and a written list of what you refuse to own until it is fixed.

The dependency layer gets its own treatment in our dependency diligence pass, which converts lockfile age and unmaintained transitive packages into the same engineer-day language the risk register uses.

Frequently Asked Questions

How long should a codebase audit take before agreeing to maintain someone else's software?

Two weeks of one or two engineers is enough for most small and mid-sized systems to produce a system map, an integration inventory, and a priced risk register. Shorter than a week and you will miss anything that only runs weekly or monthly. Longer than three weeks and you are doing work that belongs in the first sprint of actual maintenance.

What should be in a handover document from an outgoing development team?

At minimum: a list of every deployed environment and its URL, credential locations for all third-party services, every scheduled job with its schedule and purpose, the release procedure including manual steps, and a written summary of the last few production incidents. Anything beyond that is a bonus. Assume you will have to reconstruct the architecture diagram yourself.

Can you audit a codebase without production access?

Partially. You can read source, schema, and CI configuration, and you can estimate structural risk. You cannot discover undocumented cron jobs, unknown webhook consumers, environment bleed, or real traffic patterns, and those are where the expensive surprises live. We treat read-only production observability access as a precondition for a meaningful audit rather than a nice-to-have.

What do you do if the outgoing developers are hostile or already gone?

Fall back entirely on instrumentation and the database. Log every inbound and outbound request, enumerate scheduled jobs from the hosting console and crontab, and reconstruct the domain model from schema and row distributions. Then substitute operations staff for the outgoing engineers: the people using the system daily know the manual workarounds and the monthly rituals, which is where most undocumented business logic actually lives.

Should the audit period be billed separately from the maintenance contract?

Yes, and we recommend it in both directions. Billing it separately lets the client walk away with a useful system map and risk register even if they hire someone else, and it lets the incoming team quote maintenance from evidence instead of optimism. Bundling the audit into a maintenance retainer creates pressure to underprice the findings you just discovered.

Filip Lauc

Written by

Filip Lauc

CEO, Jaspero

Filip Lauc is the CEO of Jaspero, a software development agency based in Osijek, Croatia. A full-stack JavaScript developer with over a decade of experience across Angular, Svelte, and Node.js, he leads Jaspero's work as a long-term embedded engineering partner for clients like GlycanAge, where his team has served as the dedicated engineering team for six years.

Let's Build Together

Your vision,
our expertise.

From AI integration to full-stack development, we turn ambitious ideas into products that perform.