Software Maintenance & Support
Keeping working software working — patched, monitored, and safe to change. We take over systems we did not build, run them under a clear severity model, and spend a reserved share of every month paying down the debt that causes the incidents.
Who this is for
- The people who built our system have left, and now nobody is confident enough to deploy it.
- We have a working product and no engineering team — we need someone on the hook when it breaks.
- Our internal team is busy with the roadmap and dependency updates keep getting pushed to next quarter.
- We have one bug and one small change, and we need someone senior to look at it without signing a year-long contract.
What usually goes wrong
The problems this work exists to solve
Nobody can explain how the system runs any more
The first work on a takeover is not a fix, it is a reproduction. We build the application from a clean checkout on a clean machine and write down every undocumented step it needed — the environment variable that only exists on the production box, the migration run by hand, the cron job on a VM nobody listed. That produces a system dossier: service map, data stores, third-party dependencies and who owns each account, deploy path, and the sequence to bring everything up from cold. Until that exists, every estimate we give you is a guess and every deploy is a coin toss.
Dependency updates have been deferred so long that upgrading is now a project
Deferred patching compounds: the jump from an old major version to current is far harder than a chain of small ones. We restart the clock in waves rather than one heroic upgrade. Wave one is patch and minor versions with lockfiles committed and automated update PRs, so the diff is small and the blast radius is visible. Wave two is the framework and runtime majors, each with its own branch, its own migration notes and its own release. Base container images get rebuilt on a schedule too — OS-level CVEs sit in images long after the application dependencies are clean, and that is the layer teams forget.
Alerts fire constantly and everyone has learned to ignore them
Alert fatigue is a design fault, not a discipline problem. We delete alerts on causes that are not user-visible — CPU at 80%, a single pod restarting — and replace them with alerts on symptoms: error rate, latency at the tail, queue depth that is still growing, a scheduled job that did not report success. Every alert that pages a human gets a runbook link and a stated action; if we cannot write the action, the alert becomes a dashboard panel instead. The target is a pager that is quiet enough that a page means something.
Every small change feels risky because there is no safety net
On inherited code we do not start by writing tests for everything. We write characterisation tests around the specific area a change request touches — tests that pin the current behaviour, correct or not — then make the change and watch the pins. Over months of change requests that produces a test suite shaped by where the system actually moves, rather than a coverage number nobody uses. Where the change is risky and reversible, it ships behind a feature flag so a rollback is a config toggle rather than a redeploy.
Support requests arrive by WhatsApp, email and hallway, and nothing gets prioritised
We insist on one intake queue and a severity model agreed before the retainer starts. Severity is defined by user impact and workaround availability, not by who is asking: a payment path that is down is not the same as a report that renders slowly, even when the same director reports both. Each severity carries its own acknowledgement path, its own escalation, and its own rule about whether it interrupts planned work. Anything that pages out of hours also gets a written follow-up in the next working day so the pattern is visible.
Technical debt never gets paid down because business-as-usual consumes the whole retainer
We split retainer capacity explicitly and hold the split. A defined share goes to incidents and change requests; a defined share goes to structural work chosen from a debt register we maintain with you — the retry that is not idempotent, the query with no index, the module that every bug report touches. If BAU overruns its share two cycles running, that is a scoping signal we raise rather than absorb silently. Debt work is invisible unless it is scheduled, and unscheduled debt is what makes maintenance costs rise every year.
Scope
What you actually get
Takeover audit and system dossier
A reproducible build, an inventory of services, data stores, domains, certificates and third-party accounts, a deploy and rollback runbook, and a written list of the risks we found ranked by likelihood and blast radius. Delivered as documents in your repository, not as a slide deck.
Severity model and support intake
Agreed severity definitions tied to user impact, a single ticket queue, an escalation path with named people on both sides, and a rule for what interrupts planned work. Includes the out-of-hours contact route if the engagement covers it.
Dependency and security patching programme
Automated update PRs with lockfiles, a scheduled rebuild of base images, triage of advisories against whether the vulnerable code path is actually reachable in your system, and a queued upgrade plan for anything approaching end of life.
Monitoring and alerting you can act on
Instrumentation for error rate, latency distribution, saturation and traffic; dashboards for the handful of screens that matter during an incident; symptom-based alerts, each with a runbook. Built on your existing tooling wherever it is workable.
Small change requests
The steady stream of copy changes, new report columns, extra validation rules, tax and rate changes, integrations with one more provider. Estimated, queued, shipped and released inside the retainer rather than raised as separate projects.
Technical-debt paydown lane
A maintained debt register with each item costed and its consequence stated, and a reserved share of every cycle spent working through it. Items are chosen with you, so the lane stays connected to what is actually hurting.
Cycle report and quarterly review
What broke and why, what shipped, what was patched, what moved on the debt register, and where the hours went. Quarterly we revisit the honest question of whether continuing to maintain a given component still beats replacing it.
Technology
Technology options
We are technology-agnostic. These are the choices we reach for, and how the decision actually gets made.
Monitoring, tracing and alerting
The choice is mostly about who carries the operating burden. Hosted tools cost money every month but nobody has to babysit a metrics store; self-hosted Prometheus and Grafana are cheap in licence terms and become another thing you maintain. We instrument through OpenTelemetry either way, so the backend can change later without touching application code.
Dependency and supply-chain tooling
The real decision is what auto-merges. Patch-level updates on well-tested libraries can merge automatically once CI is trustworthy; anything that touches auth, payments or data access gets human review regardless of semver. Auto-merging into a suite you do not trust just automates the introduction of bugs.
Runtime and deployment
On a maintenance engagement we default to keeping the platform you have. Moving a stable system to a new runtime is a modernization project wearing a support badge. We propose a platform change only when the platform is demonstrably causing the incidents — no rollback path, no reproducible deploy, or an operating system past its support window.
Data, backup and restore
Backups are not the deliverable; a measured restore is. We time a real restore into a scratch environment and report the number, because that number is your actual recovery objective. Tighter objectives cost more in storage and replication, so we set them against what an hour of lost data is genuinely worth to you.
Ticketing and knowledge
We work in your tracker rather than asking you to adopt ours, with one condition: a single queue. Documentation lives in the code repository next to what it describes, because a wiki drifts from reality within a couple of releases and a runbook that lies is worse than no runbook.
How it runs
The shape of the engagement
- 01
1. Access, credentials and a clean build
We collect access to code, cloud accounts, DNS, certificates, app stores and third-party dashboards, and rotate every shared credential the previous team held. Then we build and run the system from a clean checkout. What that exercise breaks on becomes the first section of the dossier.
- 02
2. Read the system and rank the risk
A read-only pass over the code, schema, deploy path and recent incident history. The output is a ranked risk list — unsupported runtime, missing backups, hardcoded secrets, an unindexed query on a growing table — with each item costed. You decide what gets fixed first; we make sure the list is honest.
- 03
3. Stabilise before improving
Backups verified by an actual restore. Alerting cut back to symptoms with runbooks attached. A rollback that has been rehearsed at least once. This phase is deliberately unglamorous and it is what makes every later change cheaper.
- 04
4. First patch wave and first change requests
Security and patch-level dependency updates go out in small releases while we take the earliest change requests. This is where we learn how the system behaves under change, and where your team sees our release rhythm before the retainer settles.
- 05
5. Steady state on a fixed cadence
A regular cycle with capacity split between support, change requests and debt paydown. Planned work is scheduled, incidents interrupt it under the severity model, and the split is reported at the end of each cycle so overruns are visible rather than absorbed.
- 06
6. Quarterly review and the rebuild question
Every quarter we look at where the hours went and which components are consuming disproportionate effort. If a component's maintenance cost is climbing faster than its value, we say so and scope the replacement — even when that shrinks the retainer.
Engineering
Architecture, security and performance
The decisions that are expensive to change later, and where we stand on them.
Architecture
- We do not rewrite what we were hired to keep running
- The default answer to ugly code that works is to leave it alone. Rewrites on a support retainer are how a system that was merely inelegant becomes a system that is also broken and half-finished. When a component genuinely must change, we change it behind a stable interface and cut over one caller at a time, so the old path stays available until the new one has survived real traffic. The exception is a component that is both unsafe and unfixable in place — an unsupported runtime, or a dependency with no maintained successor.
- A reproducible build comes before every other improvement
- If the system can only be built on one laptop or deployed by one person, nothing else we do is durable. We pin the toolchain, commit lockfiles, containerise the build if it is not already, and prove it by building from a clean machine. This is finding number one on most takeovers and it is the difference between a maintenance engagement and an indefinite dependency on whoever holds the tribal knowledge.
- Change requests should land in seams, not in the middle of the system
- Over a few months of BAU a pattern emerges: the same three areas absorb most change requests. Those are where we invest. We put an adapter around the volatile external integration, extract the rules that the business keeps rewriting into one place, and leave the stable core untouched. Refactoring guided by where change actually lands beats refactoring guided by what looks worst.
- Configuration and secrets come out of the code early
- Inherited systems commonly carry API keys in the repository and environment differences expressed as if-statements. We move configuration into the environment and secrets into a managed store, then rotate anything that has ever been committed — history is not a hiding place. This is cheap work that removes a whole class of incident and makes staging a real rehearsal rather than an approximation.
- Maintenance versus rebuild is an arithmetic question
- Rebuild becomes rational when the cost of a typical change is rising cycle over cycle, when a majority of incidents cluster in one component, or when the platform is past vendor support so patches simply do not exist. It is not rational because the code is old, or written in a language currently out of fashion, or because a new team finds it unfamiliar. We put the numbers from your own change history against that question rather than arguing it on aesthetics.
Security
- Advisories are triaged by reachability, not by score alone
- A high-severity CVE in a code path your application never calls is a lower priority than a medium-severity one in your authentication flow. We check whether the vulnerable function is actually reachable, whether the affected input is user-controlled, and whether an existing control already blocks it. That triage is what makes a patch programme sustainable — treating every advisory as an emergency means the real emergencies get the same tired response.
- Credential rotation is part of taking over, not an optional extra
- When a system changes hands, every credential the previous holders knew must be assumed compromised: database passwords, cloud keys, payment gateway secrets, app store accounts, DNS registrar logins. We rotate them, replace shared personal logins with service accounts and scoped keys, and record who holds what. Skipping this leaves an access list that nobody can enumerate, which is the state most inherited systems arrive in.
- End-of-life runtimes get an upgrade plan, not a risk acceptance
- An operating system, language runtime or database past its support window receives no security patches at all, which means no amount of scanning helps. We identify those first and schedule the upgrade as retainer work rather than letting it sit on a risk register being re-accepted every quarter. Where a hard dependency blocks the upgrade, we isolate the component at the network layer and say plainly what residual risk remains.
- The supply chain includes the layers below your code
- Lockfiles and application dependencies are the visible half. The other half is base images, system packages, build-time tooling and CI runner permissions. We rebuild images on a schedule so OS packages refresh, pin image digests rather than mutable tags, and keep CI credentials scoped to what a pipeline needs. A build system with broad production access is a security boundary most teams have not noticed they crossed.
- Access and audit are designed for the day something goes wrong
- We work to least privilege with a documented break-glass route for incidents, and make sure privileged actions are logged somewhere the person taking the action cannot quietly edit. For systems handling payments or health data, the retainer keeps the technical controls the regime expects in working order — access review, log retention, encryption settings — rather than letting them decay between audits.
Performance
- Baseline first, so drift is distinguishable from a bad release
- At takeover we record latency distributions, error rates and throughput for the main user journeys. Without that baseline, every complaint about slowness becomes an argument. With it, we can tell whether the system got slower last Tuesday or has been degrading for a year, which points at completely different fixes.
- Most slowdowns in maintained systems are data growth, not code
- Code that was written against a small table stays correct and gets slower as rows accumulate. We look at query plans on the tables that grew, add the indexes that were fine to omit at launch, and where a table is unbounded by design — event logs, audit trails, notification history — we introduce archival or partitioning rather than buying a larger database instance every year.
- Alerts watch symptoms; dashboards watch causes
- Pages fire on what a user would notice: requests failing, the slow tail of the latency distribution, a queue whose depth is still climbing, a scheduled job that did not report success. Resource metrics stay on dashboards where they help explain an incident. This split is what keeps the pager credible and stops incidents being discovered by customers first.
- Cost is a performance dimension on a long-running system
- Maintained systems accumulate expensive residue: environments nobody uses, oversized instances from a load test two years ago, log retention set to forever, storage classes never revisited. We review it periodically because on a system with steady traffic the infrastructure bill is one of the few numbers that only ever goes up unless someone looks.
- Recovery time is measured, not assumed
- We restore a backup into a scratch environment and time it. The measured figure becomes the recovery objective you actually have, which is frequently not the one written in a contract. If the gap matters, we close it deliberately — replication, more frequent snapshots, a rehearsed failover — knowing what each option costs to run.
Size of work
Where your project probably sits
A rough map so you can locate yourself before talking to us. Actual scope comes from a conversation, not a table.
| Level | Example | Engagement shape |
|---|---|---|
| Focused task | One reproducible bug, a dependency upgrade that broke a build, a report that needs two extra columns, or a certificate and deploy path that stopped working. | Quoted per task, no retainer commitment, usually delivered within days: the fix, the regression test that pins it, and a note on what caused it. From about Rs 25,000. |
| Light retainer | A stable internal tool or a launched product with modest traffic: patching, monitoring, backup verification and a small stream of change requests. | A fixed block of capacity each month, one named engineer, business-hours cover during Mon–Fri 10:00–19:00 IST, a monthly report and a quarterly review. Rolls month to month. |
| Standard support retainer | A revenue-carrying product with real users, integrations with payment or messaging providers, and a backlog of small changes the internal team cannot get to. | Reserved capacity per cycle with an agreed split between support, change requests and debt paydown. Two engineers so there is always cover, extended-hours response under the severity model, shared incident channel with your team. |
| Managed ownership with on-call | A platform where downtime has direct financial or clinical consequence, or a client whose in-house team has no operations capability at all. | A named team that owns the system's operation, a genuine rotation rather than one person's phone, rehearsed runbooks, escalation into your business for product decisions, and a standing quarterly roadmap alongside BAU. Priced against the rotation, not against ticket volume. |
Honest limits
When this is not what you need
Telling you this early is cheaper for both of us than discovering it in month two.
- Systems that should be replaced rather than maintained. If the platform is past vendor support, if most incidents cluster in one component, or if the cost of a routine change is rising every quarter, a support retainer just makes the decline comfortable. That is a conversation about modernization, and we would rather have it early than bill you for eighteen months of holding the line.
- Buyers who need a signed uptime or response figure before anyone has seen the code. We will agree a severity model and an escalation path up front, and we will commit to specific numbers once we know the deploy path, the recovery time and where the single points of failure are. Numbers promised before that are marketing, and the first serious incident exposes them.
- Teams looking for the cheapest available person to sit on a ticket queue. We staff maintenance with engineers senior enough to change the system safely, which is more expensive per hour and considerably cheaper per outcome. If the requirement is volume of tickets closed at the lowest rate, a support desk will serve you better than we will.
- Engagements where we cannot get production access, cannot rotate credentials, or where no one on your side can decide what ships. Maintenance is a joint activity; without an owner who can approve a release and answer a business question during an incident, we can diagnose but we cannot fix.
Related work
Projects in this space
Payment platform
A payment system built with the card data path isolated to keep PCI DSS scope small — the kind of boundary that has to be preserved through every later change.
Read the case study: Payment platformPatient records integration
Clinical records integrated across systems, where interface contracts and access controls have to keep working long after the build phase ends.
Read the case study: Patient records integrationHeadless storefront migration
A storefront migrated to a headless architecture — an example of cutting over incrementally rather than replacing a working system in one step.
Read the case study: Headless storefront migration
Questions
Common questions
What does a maintenance retainer actually cover?
Four things: keeping the system patched and its dependencies current, watching it and responding when it breaks, delivering the steady stream of small change requests, and spending a reserved share of each cycle paying down technical debt. The split between those is agreed with you and reported back each cycle. New feature development at any real scale sits outside the retainer and is scoped as project work, because mixing the two is how support quietly stops happening.
Will you commit to a response-time SLA?
We commit to a severity model from day one — what counts as critical, who is contacted, how it escalates, and whether it interrupts planned work. Specific response and resolution figures come after the takeover audit, once we know how the system deploys, how long a restore takes and where the single points of failure sit. Committing to numbers before seeing the system would mean either padding them heavily or missing them, and neither helps you.
Can you take over a system built by another agency or a developer who has left?
That is common in this work. We start by rebuilding the application from a clean checkout, which surfaces the undocumented steps, then produce a system dossier and a ranked risk list before touching anything. Handover from the previous team is helpful but not required — we assume we will not get it, and the audit is designed to work without it.
What is the smallest piece of work you will take on?
A single bug or a single small change. Focused work of that kind starts around Rs 25,000 and is quoted per task, with no retainer commitment. A single well-defined task is a legitimate way to start, and per-task work can continue indefinitely — some systems genuinely never need more than that.
Do unused hours roll over?
Reserved capacity is what makes a support engagement work — engineers who know your system have to be available, and availability costs whether or not you use it that month. We do not carry unused capacity forward indefinitely, but a light month usually gets redirected into the debt register rather than wasted, and if several consecutive cycles come in under the reservation we will suggest resizing the retainer downward.
Should we maintain this system or rebuild it?
Look at the trend in your own change history rather than at how the code looks. Rebuild when the cost of a typical change is rising cycle over cycle, when incidents concentrate in one component, or when the runtime or platform no longer receives security patches. Maintain when the system is boring, patchable and does its job — age alone is not a reason. We will give you a straight answer at the quarterly review, including when it reduces the work we are being paid for.
Do you provide out-of-hours or 24x7 on-call?
Yes, on engagements where it is justified and staffed as a genuine rotation rather than one person's mobile number. Real on-call needs rehearsed runbooks, an escalation route into your business for decisions we cannot make, and enough engineers to rotate humanely. Where downtime overnight is inconvenient rather than costly, extended-hours cover under the severity model is usually the better purchase.
Keep reading
Related work and reading
Related services
- Software Modernization
When the audit shows a component is past the point where maintenance is the economical answer, this is the replacement path — incremental migration behind stable interfaces rather than a rewrite.
- Software Testing & QA
Inherited systems usually arrive with little automated coverage. This covers building the test and CI safety net that makes routine maintenance releases low-risk.
- Cloud & DevOps
Monitoring, deploy pipelines, infrastructure as code and cost control — the operational layer a support retainer depends on when it is missing or unmanaged.
- Dedicated Development Team
When maintenance grows into a steady roadmap, a dedicated pod gives you consistent engineers for feature work alongside the support commitment.
- Software Architecture Consulting
For the maintain-versus-rebuild decision on a larger estate, where the question is which components to keep, replace or retire and in what order.
Tell us what you are running
Send us what the system is, roughly what it does, and what is worrying you — the developer who left, the dependency nobody has updated, the alert that fires every night. We will tell you whether it needs a retainer, a one-off fix, or an honest conversation about replacing it. If you already have a specific bug or small change, describe it and we will quote that task on its own.