Test First, Discover Second, Build Third: A Practical Strategy for Brownfield and Legacy Software Engineering

August 30, 2026 · 12 min read

One of the most common mistakes in legacy software is asking, “How long will this take?” before asking two more important questions:

“Why does this change matter to the business?”

“How will we know it works?”

In greenfield development, teams can often change the design as they learn. In brownfield systems, the problem is different. The software already has customers, workflows, integrations, assumptions, data models, operational habits, and years of accumulated behavior. Much of that behavior is not documented anywhere except in the code and in the heads of the people who have worked with it for years.

That changes the order in which engineering should happen.

For meaningful changes to legacy systems, a major database change, a deeper UX or workflow change, anything whose blast radius is not obvious at a glance, I use a four-step approach:

  1. Define the capability and its outcomes in business terms.
  2. Define how the change will be evaluated.
  3. Run a bounded discovery and architecture phase.
  4. Implement incrementally against the evaluation criteria.

The important part is the order.

1DefinetheoutcomeRevenue, churn, sales2ProveitworksEvaluation criteria first3ReduceuncertaintyBounded discovery4BuildincrementallyAgainst the criterianot: write code first, ask questions later
The order matters: outcome, proof, discovery, then implementation.

Start With the Business Outcome

Before anyone discusses architecture, estimates, or test plans, ask the question that justifies the work at all: what will this new capability do for the business that today’s system cannot?

Be concrete. How will the change drive revenue? How will it reduce churn? What will sales be able to say that they cannot say today? What support burden does it remove? If the answer is a shrug, the technical plan, however elegant, is premature.

This is not a ceremonial step. Stating the capability in terms of sales and support does two things. It forces the requester to defend the investment in units the business actually measures, and it gives the engineering team the context they need to make a thousand small tradeoffs correctly later. An engineer who knows the change exists to cut onboarding churn will make different decisions than one who was simply handed a ticket.

If the outcome cannot be stated in business terms, stop here. The cheapest engineering is the work you decide not to do.

Then Define How You Will Prove It Works

Before substantial code is written, ask the team to explain how the resulting change will be tested. Not merely, “What unit tests will you write?” Ask:

  • What existing behavior must not change?
  • Which workflows could break?
  • What data relationships must remain valid?
  • What integration behavior must be preserved?
  • What security or permission assumptions are involved?

Then the risks and edge cases:

  • Which edge cases matter?
  • What operational processes depend on the current behavior?
  • What would constitute a failed implementation even if the feature technically works?

And finally, who validates what:

  • What can be tested automatically?
  • What requires QA, operational, or customer validation?

This has an important secondary benefit.

If an engineer cannot describe how the change will be evaluated, the engineer probably does not yet understand the requirements well enough to estimate the work.

The test strategy therefore becomes an early measure of understanding. Good evaluation criteria convert vague requirements into something observable. Once the team can explain what success looks like, estimation usually improves, because the work is no longer an abstract feature. It is a collection of behaviors that must be created, preserved, and proven.

QA Should Move Left

In many organizations, QA is treated as the group that receives code after developers are finished. That is particularly dangerous in legacy systems.

QA should participate before implementation begins. The people who understand testing often know where the system breaks, which workflows are fragile, which customer configurations behave strangely, and which supposedly minor changes have caused trouble before.

A senior engineer may understand the architecture better than anyone else. An experienced QA engineer may understand the system’s observable behavior better than anyone else. Both forms of knowledge matter.

The first output of complex brownfield work should therefore often be a lightweight validation plan rather than a feature branch.

Discovery Is Real Engineering Work

The second mistake is treating discovery as something engineers do informally while they are already coding. For difficult legacy changes, discovery should be explicit and bounded.

Give the engineers time to:

  • inspect the existing implementation;
  • trace important dependencies;
  • examine the data model;
  • identify hidden coupling;
  • experiment with uncertain portions;
  • create small technical spikes;
  • determine the likely blast radius;
  • map implementation work to test cases;
  • identify which work can be delegated;
  • identify which work genuinely requires specialized knowledge.

The purpose of this phase is not to finish production code. The purpose is to turn unknowns into knowns.

That distinction matters because organizations often pressure engineers to estimate work while simultaneously denying them the time required to understand the work. Then everyone acts surprised when the estimate is wrong. A short, deliberate discovery phase is usually cheaper than a long implementation based on incorrect assumptions.

Estimates Should Be an Output of Discovery

For uncertain legacy work, estimates should not always be an input into engineering. They should often be an output of engineering discovery.

Instead of asking, “When will the entire feature be finished?” ask: “How much investigation do we need before we can provide a useful delivery range?”

Then ask the team to produce:

  • known work;
  • unresolved risks;
  • important dependencies;
  • likely implementation increments;
  • testing requirements;
  • the assumptions behind the estimate;
  • confidence ranges.

The business still gets dates. It simply gets dates with some intellectual honesty behind them. That is much better than selecting a distant date because everyone knows the real estimate is unknowable.

Implementation Comes Fourth

Only after the outcome, the evaluation strategy, and the discovery should substantial implementation begin. At that point the team has something valuable that it did not have before: a definition of success.

Implementation can then proceed incrementally. For each meaningful increment:

  1. Build something.
  2. Run it against the previously defined evaluation criteria.
  3. Learn what was wrong about the assumptions.
  4. Update the architecture or estimate if necessary.
  5. Continue.

This turns the original test strategy into the governing framework for development rather than something QA invents after the code has already been written.

Evaluationharnesswritten before the codeBuild an incrementRun the evaluationLearn what was wrongUpdate plan, estimateThe loop is the planCriteria come first.Every increment is testedagainst them, not againstwhatever QA invents afterthe code is written.
The evaluation harness governs every increment of the build.

The “Only I Can Build It” Problem

Legacy systems frequently produce indispensable engineers. Someone has been around for years, remembers why strange architectural decisions were made, understands undocumented customer behavior, and knows where the bodies are buried.

That expertise is enormously valuable. But “only this person can build it” should never become an accepted permanent operating model.

“Only this person can build it” should trigger the opposite reaction. It identifies concentrated organizational risk.

The experienced engineer may genuinely be the only person qualified to lead the change today. But leadership and implementation are not the same thing. That engineer can define evaluation criteria, explain historical constraints, identify architectural risks, conduct technical discovery, design the approach, review implementation, handle the hardest portions, and teach other engineers.

Other people can increasingly perform the implementation. That is how institutional knowledge becomes organizational capability instead of individual dependency.

Use Test Cases to Transfer Knowledge

Traditional knowledge transfer often consists of documentation, meetings, or senior developers explaining code to junior developers. That helps, but executable evaluation criteria are often more powerful.

A good test explains something very specific about what the system must do. If the experienced engineer can describe the important behaviors precisely enough that QA, another engineer, or an automated system can evaluate them, much of the expert’s implicit knowledge has been converted into an organizational asset.

This is where test-driven thinking becomes useful beyond software quality. Tests become knowledge-transfer mechanisms. They explain the contract of the system. And that becomes particularly important as AI coding tools improve.

AI Changes Who Has to Write the Code

AI makes the distinction between understanding a system and typing code increasingly important. The person with the deepest legacy knowledge may still be indispensable for deciding what behavior matters, identifying dangerous assumptions, recognizing hidden dependencies, defining success, and reviewing architectural choices.

That does not mean that person must implement every change personally.

If requirements, constraints, acceptance criteria, and test cases are sufficiently explicit, other engineers, and increasingly AI-assisted workflows, can perform large portions of the implementation. This suggests a rule for AI-era brownfield development:

The scarcer the expertise, the more of it should be spent defining and evaluating the change rather than typing every line required to produce it.

The better the evaluation harness, the more implementation can safely move to other humans or to AI. This is the same argument I make in Customer-Led Engineering: as code generation gets cheaper, judgment, specification, and evaluation get more valuable. Brownfield work is where that shift pays off first, because legacy systems are exactly where the specification was never written down.

Scarce expertiseDefines the business outcomeDefines evaluation criteriaLeads discovery, spots riskReviews, teaches, takes thehardest partsOther engineers + AIImplement against explicit,objectively testable criteriaThe better the harness,the more work moves here.specreview“Only I can build it”= concentrated organizational risk
In the AI era, scarce expertise defines and evaluates; implementation delegates.

Protect Experts From Their Own Helpfulness

Another common failure mode appears when the most knowledgeable engineers are also the most helpful people in the company. They get pulled into production issues, maintenance tickets, architecture questions, customer escalations, compliance work, code reviews, meetings, new features, and troubleshooting. Then leadership asks them why the important strategic project is late.

This is not primarily an estimation problem. It is a capacity-management problem.

Senior expertise should be deliberately allocated. “Involved” should not mean “personally participates in everything.” Sometimes the highest-value contribution from a senior engineer is thirty minutes of architectural guidance that saves another engineer three days.

Engineering leadership therefore has to distinguish between work only the expert can perform, work where the expert should advise, and work the expert should stop doing. Otherwise institutional knowledge turns into institutional congestion.

Brownfield Engineering Is Really Uncertainty Reduction

The deeper principle is that complex legacy development is not primarily a coding problem. It is an uncertainty problem. Unknown requirements. Unknown dependencies. Unknown customer configurations. Unknown historical assumptions. Unknown regression risks. Unknown operational consequences.

Asking engineers to code faster does not remove those unknowns. Good brownfield engineering systematically removes them.

  • The business case removes uncertainty about purpose.
  • Evaluation removes uncertainty about success.
  • Discovery removes uncertainty about the system.
  • Incremental implementation removes uncertainty about whether the proposed solution actually works.

That leads to a useful operating principle: do not begin complex legacy work by asking for code or dates. Begin by asking what needs to become known. Then organize the engineering process around discovering those answers.

The Practical Brownfield Playbook

For substantial legacy-system changes:

  1. Define the capability in business terms first: revenue, churn, sales, support. If you cannot, stop.
  2. Define the evaluation strategy before implementation.
  3. Bring QA into the work before code is written.
  4. Convert important legacy behavior into explicit tests and acceptance criteria.
  5. Run a short, bounded discovery phase before making confident estimates.
  6. Treat estimates as ranges with assumptions and confidence, not promises.
  7. Break implementation into observable increments.
  8. Test each increment against the original evaluation criteria.
  9. Use senior legacy experts as architects, historians, reviewers, and teachers, not necessarily as the sole implementers.
  10. Treat “only one person can do this” as a risk the project should deliberately reduce.
  11. Use AI and other engineers wherever the work is sufficiently specified and objectively testable.

The result is not necessarily faster coding. It is something more valuable in brownfield software: fewer expensive surprises.

And over time, it produces a system that is easier to understand, easier to change, easier to test, easier to delegate, and increasingly easier for both humans and AI to maintain.