(Notes)

Taking Over an Existing System: A Full Code Audit from Candidate Findings to a Usable Report

Start a takeover with a full code audit, and make the last step an attempt to overturn the audit's own findings. The report then keeps only the problems a customer will actually run into.

Polish Design engineering team・2026-01-20

Contents

When a project changes development teams, the largest unknown is neither the quote nor the pitch deck. It is the state of the code. What the previous team left behind, which parts can be trusted, and which parts break when touched are rarely things anyone can explain on handover day. How to choose the new team is covered in how to choose a software development vendor. This article deals with the most concrete step after that choice: turning the state of the code into information a decision can rest on.

Software does not stand still on launch day. Parnas, 1994 proposed the idea of software aging: aging cannot be avoided, but its causes can be understood, its effects limited, and part of it reversed, so long-term product health deserves more attention than the first release. Lehman, 1980 points in the same direction with the law of continuing change: a program that is used and has to reflect reality becomes less useful unless it keeps being adjusted, and changes made without maintenance work degrade structure and add complexity. A system that has been in production for some time sits in exactly that state. People use it, it has been modified many times, and the reasons for the modifications mostly are not written in the code.

Changing teams is also a supplier risk. NIST SP 800-161 gives practices for identifying, assessing, and reducing the risks that suppliers and procurement bring to systems, products, and services, such as malicious functionality, counterfeits, and poor development quality (Boyens et al., 2024). Whether the previous team's system is reliable belongs in the category that has to be identified and assessed. A full code audit is one way to assess it: no impressions, just reading the code.

The Audit Process: Scan, Candidates, Rebuttal, Report

A full code audit covers the entire codebase, not a sample of pages. The process runs in four stages. In the first, the system is divided into seven areas that are scanned in parallel, each producing candidate findings. In the second, every candidate is verified against the code to confirm that the problem exists. In the third, an adversarial review deliberately looks for reasons to overturn the audit's own findings, and the reviewer has to settle two things: why that piece of code is there, and what happens if it is taken out. In the fourth, what remains is written into the report.

There is no single correct way to divide the areas. OWASP ASVS, 2025 is a list of verification requirements that developers can check against while building, coding, and testing web applications and services, and it helps confirm that common verification topics are not missed. The OWASP Top 10, 2025 is a consensus list of the most serious security risks to web applications, with the 2025 edition marked as the current one; during severity rating it serves as a cross-check that the most serious class of risk has not been overlooked.

A person stands at an open blue cabinet and plugs an orange cable into the equipment inside.

This asks a different question from vulnerability scanning or penetration testing. The differences between the two, and when each fits, are laid out in vulnerability scanning versus penetration testing. A code audit reads the code itself, so it can see things that rarely surface while the system runs: a fallback that has never been triggered, an error that is swallowed, and what the program does when a setting is left blank.

Fig. 1Nearly 10% of about seventy candidate findings were overturned before the report
  1. Candidate findingsabout seventyMerged from seven parallel scans, each finding verified against the code
  2. Overturned in rebuttalnearly 10%Mostly patches that earlier developers left on purpose
  3. Kept for the reportabout sixtySorted into red, yellow, and green

One full audit; figures are approximate

In one full audit, the merged list of candidate findings came to about seventy. After the rebuttal step, nearly 10% were rejected and the rest went into the report. The rejected ones were not wasted effort. Each leaves behind the reasoning for why it looked like a defect and was not, so a later maintainer does not remove the same patch as if it were an error.

Rebuttal Is Where a Report Gets Its Credibility

Teams taking over a system tend to worry about two things in an audit report: padding and false alarms. Padding means listing everything that looks suspicious until the few findings that matter are buried. The rebuttal step exists to filter that out. Most rejected candidates looked like defects but were patches the previous developers had left in on purpose. Read in context, that code was added to deal with a specific problem, and taking the patch out brings the problem back. A well-meant fix then causes an incident.

A person grips a large wrench with both hands to turn the nut at the center of a yellow gear, with a smaller teal gear meshing beside it.

False alarms do more than waste time; the larger cost is lost trust. Sadowski et al., 2018 summarizes experience building static analysis tools at Google and states that false positives make users distrust analysis results; for checks that run during code review the criterion is an effective false-positive rate below 10%, and compile-time checks must produce no effective false positives. An interview study of developers also found that the way false positives and warnings are presented is one of the main reasons developers do not use static analysis tools, even though interviewees generally thought the tools helped (Johnson et al., 2013). Both studies concern automated tools rather than manual audits, so the figures cannot be compared directly. The principle carries over, though: every finding in a report that does not hold up lowers the reader's trust in the findings that do.

A Report Should Say What Customers Will Run Into

How a report is written decides whether it gets used. A list of technical terms is legible only to people who write code; the person responsible for the product needs to know what users or customers will run into. A usable report sorts findings into red, yellow, and green. Red is for problems that can hurt people, lose data, or invite attack, and each red finding is written in four fields: what the user or customer will experience, when it happens, who should fix it, and the estimated time. Red entries read like this:

  • Admin login has no attempt limit, and failed attempts are not logged: an account can be tried over and over, with nothing to trace afterward.
  • A person whose access was revoked can still change data for a period afterward: disabling the account did not block the session at once.
  • A deployment command permanently deletes files that are not under version control: the next deploy removes the files, with no way to recover.

Yellow lists problems that do not yet rise to red. Green is a separate list of what is done well and must not be taken apart by later maintainers, because one class of incident after a takeover is a new team removing a deliberate earlier design that looked like a defect. A final section, "What Not to Do Now," names the items not worth reworking at present, so that the audit does not turn into a round of sweeping changes. This approach runs in the same direction as monitoring practice: the chapter on monitoring distributed systems in the SRE literature argues that alerts should in principle target only problems that need human intervention and affect users, and should be symptom-based first, with causes second (Ewaschuk, 2017). A report likewise states the symptom first and then where the cause sits.

Fig. 2Every finding gets a verdict: fix now, schedule the fix, or keep and record
  1. Verify against the codeConfirm the problem exists
  2. Try to overturn itCheck whether it is a patch left on purpose
  • It can hurt people, lose data, or invite attack
    Fix nowRated red, with who should fix it and the estimated time
  • It carries risk but is not yet red
    Schedule the fixRated yellow, placed in the maintenance plan after handover
  • It looks like a defect but proves to be a deliberate patch
    Keep and recordWrite down why it must not be removed, so no one fixes it by mistake

The Costliest Defects Raise No Error

The costliest defects often raise no error. The feature appears to work, automated tests are green, no error message appears, and the protection has never actually been active. Such defects are invisible at acceptance and surface only when something goes wrong. Defects of this kind share a common shape, shown here by a few de-identified examples:

  • The key for bot verification was never set, and the program, on finding it unset, switched the check off entirely and let everything through without a single log line; during the audit a fake verification code still reached the next step.
  • The condition guarding a fallback is always true, so the fallback has never been triggered.
  • An empty error handler combined with a full overwrite in the back end: a failed write is swallowed, data is silently corrupted, and the screen reports success.
  • Some framework versions do not catch errors thrown from asynchronous functions, so adding a single layer of error handling does nothing.
  • An environment variable was not set, so the system was treated as a test environment, every outgoing email was blocked, and no alert was raised.
  • Log rotation failed repeatedly and no one knew.

Three countermeasures apply. First, when a setting is missing, the system refuses to start or at least raises a clear alert: failure should make noise and leave a line in the log. Second, every fix leaves behind a check that fails when something breaks, not just a comment. The method is a negative test, which feeds deliberately wrong input to prove the check really catches the problem. Third, separate "checked" from "checked correctly." Tests run against mock data, but after a move or a configuration change the switch that points to the mock can quietly stop working, and the tests stay green while actually hitting the real system. So where a test connects is itself something to check, and the latency of the test environment is measured first so that waiting times scale with it, rather than a fixed wait producing false alarms.

Whether to Keep the Admin Panel Is Answered by Usage Records

When an admin panel is recommended as essential, or an existing one is said to be indispensable, the operation records after launch are the most direct evidence. The method is to read three months of operation records, set aside logins, logouts, and account management, and count only genuine content edits. If content edits trend toward zero, move the content into version control and keep the admin panel for viewing only. Every change then carries a version record and a reason, and there is no need to maintain a full editing interface for users who never appeared. Changes made to a database by script are invisible in the audit trail, whereas content in version control shows every change. Retiring the panel cannot be undone, so the safer path is to make it read-only and watch for a while before deciding whether to shut it down.

The First Month: Old Problems Stay, New Code Adds No Risk

In the first month after handover, the scope has to be drawn clearly. Existing problems in the old system are not repaired opportunistically, and newly written code must not introduce new risk. Information is thinnest at the start of a takeover, which makes sweeping rework the riskiest move, while new code is the one part the development team fully controls. NIST's SSDF describes secure development practices as a high-level set that can be added to any software development life cycle model, aiming to reduce vulnerabilities in released software, limit the impact of undetected defects, and address root causes (Souppaya et al., 2022). Old problems are recorded and ranked first, and new code follows a short set of concrete rules:

  • Database queries are always parameterized.
  • Reads and writes check ownership, with the query restricted to both the record and the user.
  • Callbacks from third-party services, such as payment providers, are always verified by signature.
  • Secrets stay out of version control, and logs never print secrets in plain text; only a hash is compared or a length is printed.

When AI assists with development, permissions are cut one layer further: AI does feature work only; changes to security settings, version control tracking, and deployment scripts always need the responsible lead to confirm first, and a security problem found by AI is reported, not fixed on its own. The reason is practical: when deployment relies on pulling the latest version, one wrong tracking change can cause files to be deleted, and the cost is downtime.

Fig. 3The first month sees the current state clearly and handles red findings first, without reworking old problems
Before handover
Audit
Red findings
New code
Customer
Development team
1
CustomerPrepare materialsSource code or access to it, the current deployment method, problems from the last six months
2
Development teamScan, verify, rebutSeven areas scanned in parallel, each finding verified against the code, then deliberately challenged
3
CustomerRead the report and set the orderWho should fix it and the estimated time are written in each red entry
4
Development teamFix red findings firstEach fix comes with a check that fails if it breaks
5
Development teamNew code follows the rulesParameterized queries, ownership checks, signed callbacks

Phases show order, not a fixed number of days

After the first month, the system moves into long-term maintenance. The POLISH Engineering Team covers this stage with Polish+ takeover and long-term maintenance; how long-term maintenance works in practice can be seen in the long-term maintenance case alongside a Ministry of Education project.

Keep the Real World Out of the Test Environment

After a takeover, every test has to be certain it will not touch real users. In a test environment, email goes only to the operator, or is not sent at all; when an internal interface has no key configured, or the key is too short, it answers with not found to everything outside and looks as if it does not exist. Responses carry no personal data, and an email address is looked up only on the server, at the moment of sending, never through a public interface.

Tables are sorted by nature into three levels: modifiable, read-only, and insert-only. For a modifiable table, a restorable copy is saved before any change; the table holding member personal data is read-only; every other existing table only gets new tables alongside, never replacements. The production database is touched only with read-only tools, and database change scripts must be safe to run repeatedly.

A configuration change that could take down the whole site follows a fixed procedure: back up the entire configuration, test in isolation, apply with automatic rollback on failure, and verify item by item over the public network. File sync scripts include safeguards: a script stops if the source directory is empty, so an incomplete build cannot wipe the remote; it stops if more than half of the remote files would be deleted, unless a flag says otherwise; and after every upload it compares hashes to confirm the files match.

When a Full Code Audit Does Not Fit

A full code audit is not recommended in the following situations.

  • The system is about to be replaced: the value of an audit is realized through the repairs that follow, and when the system is nearly retired, the investment does not pay off.
  • The source code is out of reach: a full code audit reads the code, and without it the work cannot proceed.
  • The goal is only a cheap maintenance contact: an audit exists to see the current state before deciding how to repair it.

What to Prepare Before a Takeover

Three things need to be ready before work starts: the source code or access to it, the current deployment method, and the problems that have occurred in the last six months. With all three in hand, the scope and order of the audit can be judged. Once the materials are ready, describe the current system and arrange a conversation with the development team.

(Contact)

Start a project.

Three steps to a clear direction and budget range.

  1. 01Book the first meeting: choose a time. A full specification is not required.
  2. 02A 15 to 30 minute online consultation: direction, budget range, and draft timeline.
  3. 03After materials are reviewed, a quote and delivery specification follows in about 3 to 5 working days.
Email
hello@polish-design.com.tw
Mailing address
3F, No. 191-1, Zhifu Rd., Zhongshan Dist., Taipei 104, Taiwan

Book the first meeting

Monday to Friday, 16:00–18:0030 minutes online

Choose a time, then confirm with a Google account or an email address. A confirmed booking can be added to the calendar.

Choose a time

Or send the details first

Not ready to pick a time yet: leave the project details here and the team replies by email. A meeting can still be booked right after sending.

More details (optional): service, stage, links, source

Information is used only to evaluate this project.

ISO/IEC 27001:2022 certificate

(Certificate)

ISO/IEC 27001:2022

Certification body
ARES International Certification
Scope
The development process for interactive immersive 3D systems, and network and infrastructure operations and management.

Verify with ARES