We Inherited a .NET Codebase Nobody Understands: Where Do We Start
July 14, 2026
The first sixty days on an inherited .NET codebase should produce a map, not a plan. Both common instincts (rewrite immediately, or freeze until the system is fully understood) fail, and they fail in predictable ways.
The rewrite instinct produces an estimate built on the documented requirements rather than the implemented behavior, which reproduces the original's defects and misses its edge cases. The freeze instinct produces months of no delivery, which exhausts organizational patience before any understanding is achieved.
Suspend the architectural judgment
A codebase running in production for years is generally more functional than it appears.
Naming is inconsistent. The architecture matches no pattern from any reference. A stored procedure performs work that belongs in the application layer. It is also running, and something it does is valuable enough that the organization has funded keeping it alive.
The operative question in the first sixty days is not whether the code is good. It is what the system does and what depends on it. Those are answerable. Code quality assessments made before they are answered are unreliable in both directions.
Get it running locally first
This sounds elementary and routinely takes a week on an inherited system. The friction encountered is diagnostic information about the codebase, collected before a line has been read.
What the process typically surfaces:
- Missing connection strings and configuration values that exist only on a production server
- A database schema that has drifted from what the code expects
- A dependency on a service reachable only from the production network
- Build steps that exist in one person's shell history
- Hardcoded paths, machine names, or certificate references
Record each of these as it is resolved. This list is the beginning of the deployment documentation, and it is the only time it will be assembled with full attention.
Trace workflows, not files
Once the application runs, follow what a user does rather than reading the code top to bottom.
Trace each primary path end to end: log in, submit the main form, run the principal report, trigger each integration. Follow the request through every layer to the database and back.
This produces a functional map before a technical one, and it anchors everything read afterward. Reading a legacy codebase without that anchor produces detailed knowledge of components with no model of how they relate, which is the state that makes changes dangerous.
Locate the risk
Parts of a codebase carry unequal risk, and effort should follow the concentration rather than the disorder.
Where the business-critical logic lives. Billing, external integrations, and state-changing operations that cannot be reversed. Failures here are noticed by the business immediately, which makes them the constraint on any change.
Where technical debt intersects planned change. Debt in code that will not be touched costs nothing in velocity. Debt in a module that is growing, an integration being extended, or a component in the path of the next initiative is what the roadmap will pay for.
What test coverage exists. On an inherited legacy system the answer is frequently minimal or none. Knowing what does exist is informative regardless. The areas the previous team covered are the areas they were least confident about, which is a direct pointer to where the system has surprised people before.
What runs on a schedule. Scheduled tasks, jobs, and services are the components most likely to be absent from any handover and most likely to have ordering dependencies between them.
Constraints for the first 60 days
Do not propose a rewrite. Not because it may not eventually be correct, but because the case cannot be made accurately yet. Rewrites proposed before a team understands the existing system consistently underestimate scope and reproduce defects that took years to find the first time.
Do not update packages or change the target framework until the deployment and testing process is understood. Dependency changes in a legacy codebase have non-obvious effects, and without coverage the failure surfaces in production.
Do not assume code that looks wrong is wrong. There is frequently a reason not visible without the history: a vendor quirk, a compliance requirement, an incident three years ago. Record it, understand it, and change it afterward.
Do make small, low-risk changes. A complete freeze is its own risk. Delivering something minor establishes that the deployment path works and that the team can change the system, which is information worth having before an urgent change is required.
The sixty-day deliverable
The output is enough understanding to make informed decisions, written down.
Document as you go: deployment steps, external dependencies and their credentials, business logic residing in stored procedures, integrations with known quirks, and the areas the previous team was visibly reluctant to touch.
At day sixty, the team should be able to walk someone through how the system behaves, name the two or three things that most need to change, and state why each one matters in business terms. That is the foundation for a modernization plan: an assessment of what the system needs, rather than a reaction to how its code reads.
Working through a problem like this?
Describe the system and where it's stuck. I'll tell you what the work actually involves.
Get in touch