PTENES
MODULE 4.1

🏚️ Safe modernization of legacy systems

AI helps a lot with understanding and converting legacy systems. But rewriting everything at once is still risky. This module is the process that replaces risk with evidence.

6
Topics
50
Minutes
Advanced
Level
Practical
Type
0%0 of 6
1

🗺️ Map Dependencies and Take Inventory

AI can map dependencies, explain code, document interfaces, and find dead code — reading a lot of code and summarizing its structure is where it delivers the most value. Start there: know the actual size of the problem before proposing a solution.

inventory dependencies characterization boundaries small change parallel execution comparison gradual migration

How to read: the entire process in one image. The cyan boxes are the evidence steps — characterize, run in parallel, compare. They turn "I think it’s the same" into proof, which is why none of them is optional.

🆕 New here?

Legacy here, it doesn’t mean "old": it means code in production that no one fully understands and that doesn’t have enough tests to be changed safely. Characterization test is a test that describes what the system does today (not what it should do). Adapter is a thin layer that translates between the old and new worlds.

Inventory

Before the plan

Dead code

Cheapest cut

Who calls whom

Defines the boundary

Risk

Decrease, don't increase

2

🔎 Identify Hidden Business Rules

Every legacy system carries rules that were never written down anywhere: historical exceptions, special cases for large customers, rounding with a tax-related reason, deadlines inherited from a 2014 contract. These are the ones that break during a rewrite.

⚠️ Typical legacy risks

• undocumented rules
• hidden integrations
• behavior used by other systems
• outdated dependencies
• knowledge concentrated in a few people
• historical exceptions
• regulatory requirements
• manual operational processes

🧪 Copyable exercise — extract the rules from the code

Objective: turn legacy code into an auditable list of rules. Choose a module you need to modernize.

Leia <caminho do módulo legado> inteiro. Não altere nada.

Produza uma tabela de REGRAS DE NEGÓCIO observadas no código, com colunas:
regra (em português, uma frase) | onde está (arquivo:linha) | condição que a
dispara | o que acontece quando não vale | parece intencional ou acidental?

Regras:
- descreva o que o código FAZ, não o que deveria fazer;
- inclua casos especiais, exceções, valores mágicos e datas fixas;
- não invente justificativa de negócio — se não souber, escreva "desconhecido";
- ao final, liste em "PERGUNTAR AO NEGÓCIO" as regras que parecem acidentais
  ou que você não consegue explicar.

How to verify: take the "ASK THE BUSINESS" list to the people who operate the system. Every rule someone recognizes becomes a characterization test in the next section; every rule no one recognizes is a candidate for removal — but only after it's confirmed.

Code = truth

Docs capture old intent

Magic value

Usually a rule

Regulatory

Doesn’t get “simplified”

Ask

Before removing

3

🧪 Write Characterization Tests

Characterization testing records current behavior — including what seems wrong. It does not judge; it takes a snapshot. It is the safety net that makes modernization possible.

✓ Well-done characterization

  • ✓Record the current output, even if it’s strange, with a comment: "current behavior, not validated"
  • ✓Covers real inputs collected from production (anonymized)
  • ✓Include edge cases: null, empty, negative, boundary date, maximum value
  • ✓Runs against the old system before of any change

✗ Pitfalls

  • ✗"Fix" the strange behavior while writing the test
  • ✗Test only the happy path—the legacy system lives in edge cases
  • ✗Write the expected result based on the documentation, not the execution
  • ✗Use production data without anonymizing it

💡 Practical tip

Generating characterization tests at scale is one of the best uses of AI: ask the agent to produce dozens of cases from real inputs, run them against the current system, and save the output as the expected result. You review the list instead of writing the tests one by one.

Captures

Doesn’t judge

Bug included

It’s also behavior

Real input

Anonymized

Volume

Where AI shines

4

🕵️ Discover Hidden Integrations

The biggest cause of post-migration incidents is simple: you turned off something “nobody used”—and someone did. A script, a spreadsheet, a report that reads the database directly, a job owned by another team.

1

Look at access, not the code

API logs, database connections, endpoint calls, file reads. Consumers show up in the traffic, not the repository.

2

Observe for one full cycle

One week isn’t enough if there’s a monthly close, quarterly report, or annual routine. The most dangerous consumer is the one that only shows up on the 1st.

3

Turn it off in stages

Notify → deprecate (with a warning on every use) → degrade → remove. Each step gives someone a chance to show up and say, “That’s mine.”

Traffic

Reveals the consumer

Complete cycle

Monthly, quarterly, yearly

Deprecate

Before removing

Silence

Doesn’t prove disuse

5

🧱 Isolate Boundaries and Create Adapters

Before changing anything, create the seam: an interface between the consumer and the implementation. With the adapter in place, you can swap the engine without touching the driver—and roll back with one line.

📐 How to choose the right boundary

  • •Few consumers (you can notify everyone).
  • •Narrow contract: few operations, simple data.
  • •Behavior already covered by characterization tests.
  • •Clear value in migrating first (known pain, not just “it’s ugly”).

⚠️ Attention

Without a defined boundary, the migration becomes a rewrite: each piece pulls in another, the scope explodes, and the project ends up in that state where the new system isn’t ready and the old one is no longer maintained. That’s the worst possible place.

Stitching

Before switching

Narrow contract

Makes switching easier

Bottleneck

Piece by piece

Easy to roll back

Change the implementation

6

⚖️ Run in parallel and compare

Both implementations receive the same input; the old one keeps responding; the new one only logs what it would respond. Every discrepancy becomes a case to investigate. This is the strongest proof there is.

Comparison panel (illustrative recreation, not an actual screenshot)

fare-calculation · old × new · 24h

requests compared .......... 148.302

identical ...................... 148.061 (99,84%)

divergent .................... 241

├─ cent rounding ............. 233 <- forgotten tax rule

├─ client with a 2014 contract ... 7 <- historical exception

└─ date 29/02 ................. 1 <- leap year

no customer response was changed (shadow mode)

How to read: 99,84% similarity is not approval — the 241 discrepancies are the actual content of the report. Each one is a rule the characterization tests missed. That’s why you run it in shadow mode before switching over.

Quick check: you’re modernizing an old module with no tests. What’s the first step?

Shadow mode

New means observe only

Divergence

It’s valuable information

Gradual

1%, 10%, 50%, 100%

Back

Available until the end

📌 Module Summary

✓
Inventory before the plan - AI is great at mapping dependencies and finding dead code.
✓
Code is the truth - hidden rules break rewrites.
✓
Characterize before changing - captures the behavior, including the weird parts.
✓
Hidden consumer shows up in the traffic - observe a complete cycle and deprecate in stages.
✓
Boundary and adapter - without them, migration becomes a rewrite.
✓
Parallel and comparison - the discrepancies are the report’s content.

Next Module:

4.2 - Agent-friendly architecture: predictable structure, AGENTS.md, and single sources of truth.