
Legacy Support
We bring operational risk in an existing application under control before larger modernisation, migration or feature work begins.
An older application is not a liability simply because it runs an earlier version of PHP or Symfony. It often holds years of business rules, exceptions and integrations: orders, payments, invoices, documents, customer accounts or internal approvals. The real risk begins when no one can tell what happened during an incident, or whether the next release will disrupt day-to-day work.
Stabilisation restores that control. It does not promise a system without failures. It reduces the likelihood and impact of failures and, when they do occur, makes them easier to detect, recover from and learn from before the next change.
Before a major programme of work, it helps to distinguish three kinds of activity. A pressing repair should not quietly become an unplanned rewrite, and a modernisation programme should not start on an unstable foundation.
IMMEDIATE PROTECTION STABILISATION MODERNISATION
limit current harm regain operational control change for the future
block a faulty operation tests for critical workflows upgrade PHP/framework
remove an exposed secret monitoring and status views replace a module/integration
stop duplicate processing repeatable deployments API boundary or migration
recovery proceduresThe edges between these stages are not always sharp, but their purpose is different. Immediate protection limits harm. Stabilisation makes the current system predictable enough to operate. Modernisation then becomes a considered choice: upgrade, migrate, replace, or leave a sound module alone.
The first question is which workflows keep the company running: sign-in and permissions, checkout, payment confirmation, invoicing, document handling, ERP imports, notifications and background work. Not every technical defect deserves the same urgency. A slow report may wait; a customer charged twice or an invisible invoice needs prompt investigation.
We assess the effect on customers and staff, how often the issue occurs, whether there is a safe manual route, reliance on an external provider and how difficult it is to restore the correct business state. This produces a practical order of work instead of a list led by the oldest-looking code.
Consider a hypothetical Symfony application that takes orders and payments, asks an accounting system to issue invoices, sends email confirmations and provides an operations panel. This is an illustrative scenario, not a GiSoft client case.
Orders may be created correctly while invoices remain in a queue after an external service slows down. A worker may continue on the previous application version after deployment. Support receives a complaint but cannot tell whether the invoice was never sent or its confirmation merely failed to return. A manual retry, made without checking the history, can produce a second document.
The first task is to establish the authoritative order state, the event that starts invoicing and the record of every attempt. The flow can then be protected against accidental repeat processing and made understandable to the people who must handle an exception.
Order → payment confirmation → persisted order state
│
▼
invoice background job
│
┌───────────────┴───────────────┐
▼ ▼
confirmed result error or timeout
update status record attempt + monitoring
│
▼
safe retry or human reviewA timeout alone does not establish that the accounting system did not act. A retry decision should use the saved state, the provider response, the ability to check its records and the business rule. Where the external action may already have happened, verification or a corrective action can be safer than an automatic retry.
A safe retry repeats the same business action without creating a new effect. A stable identifier, such as an order number or payment request ID, is useful but does not by itself guarantee idempotency.
The protection depends on its implementation: durable state, an atomic check-and-create operation, handling concurrent requests, database uniqueness constraints and the external provider’s behaviour. A sound integration can recognise an earlier attempt, return the known outcome or send a case for review. It should not hide uncertainty behind unlimited retries.
A server log is rarely enough for someone answering a customer. Technical monitoring should show worker failures, integration response times, queues, slow queries and the deployed version. An operational view should make it possible to find the business case, its processing stage, number of attempts, a safe error category and the next appropriate action.
This does not require putting complete documents or customer correspondence into logs. The useful link is between a business identifier and the minimum information needed for diagnosis and audit. Sensitive data, secrets and another organisation’s records should not be logged merely because they are convenient during debugging.
Characterisation tests first describe the application’s current behaviour, even when its code is difficult to change. A stabilisation project does not begin by writing thousands of tests. It protects high-consequence scenarios: a repeated callback does not create another charge, an unauthorised user cannot see another organisation’s data, and a failed invoice remains visible and can be handled through an agreed procedure.
Deployment deserves the same care. We document configuration and migration order, check dependency compatibility, record the application version, confirm worker restarts and run checks on critical workflows after release. A rollback plan must distinguish local changes from external effects. Reverting code or a migration cannot undo an email already sent, a payment already captured or an operation delivered to an ERP. Those cases need a verified final state, a correction procedure and a named owner.
Stabilisation does not automatically replace a database, SQL queries or integrations. The problem comes first. A slow customer list may need pagination, a smaller data set or an index after query analysis; direct SQL can be appropriate when it is clear, protected and owned.
For integrations, we make timeouts, response validation, webhook verification, credentials, responsibilities and error mapping explicit. Security work covers authentication, server-side authorisation, roles, organisation boundaries, disabled accounts, administration routes and secret storage. Hiding a button in the interface is not access control; the application must enforce permission on the server.
If only one person can deploy the application, restart its queue or repair data after an incident, the company relies on private knowledge rather than a process. Runbooks, integration ownership, access procedures, incident history and short handover sessions are part of stabilisation.
GiSoft starts with evidence: current incidents, support reports, production behaviour, deployments, failed jobs, code and dependencies. The result is an ordered risk picture, protection for the most important workflows, changes that can be verified, and a route for the next decision. It may lead to a PHP or framework upgrade, a clearer integration boundary, or leaving a stable module unchanged because its value and risk support that choice.
Stabilisation does not preserve every old technical decision. It gives the business enough control to run the application more safely and make modernisation decisions on evidence.