<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Shlomo Friman</title>
    <description>The latest articles on Forem by Shlomo Friman (@shlomofr).</description>
    <link>https://forem.com/shlomofr</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3913778%2F4635353d-57f5-4025-be4a-1ac773339572.jpg</url>
      <title>Forem: Shlomo Friman</title>
      <link>https://forem.com/shlomofr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/shlomofr"/>
    <language>en</language>
    <item>
      <title>Modernization Strategies for Critical Transactional Systems</title>
      <dc:creator>Shlomo Friman</dc:creator>
      <pubDate>Wed, 20 May 2026 17:51:07 +0000</pubDate>
      <link>https://forem.com/shlomofr/modernization-strategies-for-critical-transactional-systems-47e</link>
      <guid>https://forem.com/shlomofr/modernization-strategies-for-critical-transactional-systems-47e</guid>
      <description>&lt;p&gt;There is a version of legacy modernization that most consultants sell, and there is the version that actually happens when you are responsible for a system that processes $40 million in transactions overnight and cannot be down for more than four minutes.&lt;/p&gt;

&lt;p&gt;The difference between those two versions is where most modernization programs break apart.&lt;/p&gt;

&lt;p&gt;I have spent 27 years looking at what enterprise codebases actually mean, not what the documentation says they mean, but what the code itself reveals about the decisions that went into building it. When organizations modernize critical transactional systems, the failure mode I see most consistently is not technical. It is sequencing. They start in the wrong place, with the wrong assumptions, and then wonder why the effort stalls 14 months in with a parallel system they cannot fully trust and a legacy they cannot safely turn off.&lt;/p&gt;

&lt;p&gt;This piece is about what the right sequence actually looks like, and why the first step has almost nothing to do with architecture selection.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Systems That Cannot Tolerate a Rearchitecture Conversation First
&lt;/h2&gt;

&lt;p&gt;COBOL runs an estimated 220 billion lines of active production code today. It processes 95% of ATM transactions globally, powers the core systems of 70% of major banks, and underpins significant government payment infrastructure. These are not historical footnotes. These are systems running this morning.&lt;/p&gt;

&lt;p&gt;When we talk about modernizing critical transactional systems, we are talking about systems that have been optimized over decades for one thing: correctness under load. The irony is that the same reliability that makes them essential is what makes their replacement so difficult to plan. You cannot afford to be wrong about what they do.&lt;/p&gt;

&lt;p&gt;The standard framing in modernization projects is to begin with architecture selection. Strangler fig or big bang. Replatform or refactor. Cloud-native or hybrid. These are real decisions, and they matter, but they are second-order decisions. They depend on something that most organizations do not have before they start: an accurate picture of what the current system actually does, at the logic level, under conditions that are not in any runbook.&lt;/p&gt;

&lt;p&gt;Gartner has estimated that nearly 70% of data migration projects fail to meet their objectives. The most commonly cited causes are underestimated complexity and insufficient validation. Both of those are documentation failures, not architecture failures. You underestimate complexity when you do not know what is in the system. You fail validation when you do not know what correct behavior looks like.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Are Actually Dealing With Inside a Transactional Codebase
&lt;/h2&gt;

&lt;p&gt;A transactional system is not a codebase in the way most modern developers think about codebases. It is a sedimentary record of business decisions.&lt;/p&gt;

&lt;p&gt;A status field with 12 possible values, 5 of which no longer correspond to active business processes, is not a bug. It is a 2009 product retirement that nobody ever cleaned up, because cleaning it up would have required someone to know which downstream batch jobs were still referencing those values. Nobody wanted to find out the hard way.&lt;/p&gt;

&lt;p&gt;A nightly JCL sequence where module B must run after module A because module A writes an intermediate file that module B reads is not in any architecture document. It is in the execution pattern of the job stream, which is readable if you know what to look for, and completely invisible if you do not.&lt;/p&gt;

&lt;p&gt;A field called ACCT-TYPE with hardcoded processing logic buried in a nested EVALUATE statement is a business rule. It was probably documented somewhere in 1998. That documentation is almost certainly gone.&lt;/p&gt;

&lt;p&gt;This is what 42% of critical business logic at risk looks like in practice. It is not that the code does not contain the logic. The code contains all of it. The problem is that the logic and the reasoning behind it are not the same thing, and you need both before you can modernize safely.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Sequencing That Actually Works
&lt;/h2&gt;

&lt;p&gt;The correct sequence for modernizing a critical transactional system has three phases before any architectural work begins.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase one: codebase archaeology.&lt;/strong&gt; This is not a code review. It is not a documentation pass. It is a systematic reconstruction of what the system does, at the field level, at the job dependency level, and at the logic branch level, using the code itself as the primary source. Every hardcoded value gets catalogued and classified as either a constant or an undocumented business rule. Every conditional branch gets examined for the assumption it encodes. Every batch job dependency gets mapped. This work is slow and unglamorous. It is also the only way to know what you are actually modernizing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase two: data lineage and integrity mapping.&lt;/strong&gt; Before a single record moves, you need to know where it came from, what has happened to it, what depends on it downstream, and what the referential integrity rules are, including the ones that are enforced by application logic rather than database constraints. In COBOL environments, referential integrity is often maintained by the programs, not the database. If you migrate the data without understanding the program-level integrity rules, you will produce a technically successful migration with semantically broken data. The symptoms may not surface until six months later when a specific transaction type hits a code path that no one tested.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase three: impact analysis before any cutover.&lt;/strong&gt; Every component that touches the system needs to be identified and evaluated before the modernization scope is finalized. This includes reporting systems, downstream batch consumers, integration layers, and anything that reads data from or writes data to the system, regardless of how indirect that relationship is. The &lt;a href="https://www.in-com.com/blog/enterprise-it-asset-disposition-strategies-for-businesses-managing-data-modernization/" rel="noopener noreferrer"&gt;enterprise IT asset disposition and data modernization considerations&lt;/a&gt; that arise during this phase are significant: virtual assets such as ETL jobs, scheduled processes, and derived datasets persist across execution layers and must be accounted for before any decommissioning sequence is planned. Removing a component without mapping its downstream impact is how you get silent failures that appear weeks after cutover.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the Strangler Fig Pattern Is the Right Architecture Choice, and What It Requires
&lt;/h2&gt;

&lt;p&gt;Once the archaeology work is complete, the strangler fig pattern is the right architectural choice for most critical transactional systems. It is not the right choice because it sounds sophisticated. It is the right choice because it is the only pattern that allows you to fail small.&lt;/p&gt;

&lt;p&gt;The pattern works by routing traffic through a proxy layer that can direct requests to either the legacy system or new components depending on which module has been migrated. New microservices or services are built alongside the legacy, functionality is migrated incrementally, and the legacy is retired in pieces rather than all at once. If something fails, traffic routes back. The fallback is always the system you already know works.&lt;/p&gt;

&lt;p&gt;What is not often said clearly enough is that the strangler fig pattern requires more preparation than a big bang migration, not less. The proxy layer has to be built before any migration begins. Change Data Capture tooling has to be in place and validated before the first module is strangled. Observability across both the legacy and new execution paths has to be instrumented from day one, not added later. You need to be able to monitor latency, error rates, and business-level metrics on both paths simultaneously, because the moment you cannot see what both systems are doing, you have lost the ability to make safe routing decisions.&lt;/p&gt;

&lt;p&gt;A Tier-1 European bank that used this approach to migrate off a mainframe COBOL ledger started by migrating read operations first. Using Change Data Capture to stream mainframe transactions to a cloud-native database, they offloaded 80% of the mainframe's MIPS consumption before attempting to migrate complex write logic. The write path is where the business rules live. You earn the right to touch it by proving correctness on the read path first.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hidden Risk That Kills Modernization Programs Midway
&lt;/h2&gt;

&lt;p&gt;There is a failure mode specific to critical transactional systems that does not get discussed enough. It is not the big bang failure, where everything breaks at cutover. It is the slow accumulation failure, where nothing obviously breaks but the parallel systems begin to diverge in ways that are not immediately detectable.&lt;/p&gt;

&lt;p&gt;This happens because data in a transactional system is not just records. It is state. The state accumulated in a legacy system over 20 years reflects thousands of business events that shaped it incrementally. When you build a parallel system alongside it, the new system starts accumulating state from the migration cutover point. If the business logic that governs how state changes is not perfectly equivalent between the two systems, the databases will drift. The drift is usually not visible in individual records. It shows up in aggregate reports, in reconciliation exceptions, in the batch job that runs at month end and produces totals that are off by an amount nobody can explain.&lt;/p&gt;

&lt;p&gt;The way to manage this is to define equivalence tests before migration begins, not after. For every module being migrated, there should be a defined set of business-level outcomes that the new module must produce identically to the legacy. Not functionally similar. Identical. The comparison runs in parallel for long enough that every significant transaction type has been exercised. The module does not go live until equivalence is confirmed. This is slower than most project plans allow for. It is also the only way to avoid spending three months post-cutover explaining why the numbers do not match.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Workforce Situation Means for Timing
&lt;/h2&gt;

&lt;p&gt;By 2027, the majority of remaining COBOL-era developers will have retired. That is not a projection. That is a demographic fact about a population of developers who were already senior when the systems they built went into production. Sixty percent of COBOL-dependent organizations already identify finding skilled developers as their single biggest operational challenge.&lt;/p&gt;

&lt;p&gt;The knowledge these developers carry is not in the documentation. For the most part, there is no documentation. The knowledge is in their heads, and it is about decisions that are not visible in the code, only inferable. Why does this program skip records with a status of 7 in this context but process them in a different context? The developer who built it knows. The code does not explain itself.&lt;/p&gt;

&lt;p&gt;This is the actual urgency. Not regulatory pressure, not cloud economics, not competitive positioning. The urgency is that the people who understand what these systems actually do are leaving, and when they leave, the only record of that understanding is the codebase itself, which is readable with the right tools and the right analytical approach, but only if someone reads it before the knowledge embedded in it becomes completely orphaned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to Start
&lt;/h2&gt;

&lt;p&gt;If you are responsible for a critical transactional system and the modernization conversation has already started, the first question to ask is not what the target architecture should be. The first question is whether anyone has done the archaeology.&lt;/p&gt;

&lt;p&gt;That means: do you have a complete map of field-level business logic across the system? Do you have a dependency graph of every batch process and every downstream consumer? Do you know which parts of the codebase encode active business rules and which encode retired ones? Do you have equivalence tests defined for the modules you plan to migrate first?&lt;/p&gt;

&lt;p&gt;If the answer to those questions is no, the architecture conversation is premature. Not because architecture does not matter, but because without that foundation, every architectural decision is based on assumptions about what the system does that may not survive contact with the actual code.&lt;/p&gt;

&lt;p&gt;The systems that cannot fail are the ones that require the most preparation before any code moves. That preparation is not glamorous. It does not show up on a roadmap as a milestone. But it is what separates modernization programs that deliver from the ones that stall halfway through with two systems running in parallel, neither of which anyone fully trusts.&lt;/p&gt;

</description>
      <category>datastructures</category>
      <category>codereview</category>
      <category>analytics</category>
      <category>code</category>
    </item>
    <item>
      <title>The Hardest Part of Inheriting a Legacy Codebase Isn't the Code</title>
      <dc:creator>Shlomo Friman</dc:creator>
      <pubDate>Wed, 13 May 2026 12:18:12 +0000</pubDate>
      <link>https://forem.com/shlomofr/the-hardest-part-of-inheriting-a-legacy-codebase-isnt-the-code-46p7</link>
      <guid>https://forem.com/shlomofr/the-hardest-part-of-inheriting-a-legacy-codebase-isnt-the-code-46p7</guid>
      <description>&lt;p&gt;The first thing most developers do when they inherit a legacy codebase is open the files and start reading. That's reasonable. It's also the second-hardest part of the job.&lt;/p&gt;

&lt;p&gt;The hardest part is reconstructing everything that was never written down: the decisions, the constraints, the people, the context. The code is still there. Everything that explains why it is the way it is may be gone.&lt;/p&gt;

&lt;p&gt;I've been working with enterprise codebases since 1997. I've helped teams take ownership of systems ranging from a few hundred thousand lines to well over fifty million. The pattern that breaks projects is almost never the technology. It's the invisible inheritance: the knowledge that used to live in people, then moved into code, and is now effectively locked inside it with no key.&lt;/p&gt;

&lt;p&gt;This is what nobody tells you when you're handed the repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  There Are Two Kinds of Debt in Every Legacy System
&lt;/h2&gt;

&lt;p&gt;When developers talk about legacy technical debt, they mean the code: the inconsistent naming conventions, the god objects, the absence of tests, the framework that was already outdated when it was chosen. That debt is real, and it's measurable. Tools can scan it. Tickets can track it. Sprints can chip away at it.&lt;/p&gt;

&lt;p&gt;There is a second kind of debt that doesn't show up in any static analysis report. Call it social debt: the accumulated gap between what the code does and what anyone still alive can explain about why.&lt;/p&gt;

&lt;p&gt;Social debt accrues silently. It grows every time a developer leaves without writing down what they knew. It grows every time a business rule changes in code but not in documentation. It grows every time someone says "ask Marcus, he built that part" and then Marcus leaves. It grows when a system outlives the entire team that built it, which happens more often in enterprise software than anyone likes to admit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://getglueapp.com/blog/tribal-knowledge-software-teams" rel="noopener noreferrer"&gt;A 2024 PagerDuty report&lt;/a&gt; found that mean time to resolution increases by 77% when the responding engineer hasn't previously worked on the affected service. That number isn't measuring code complexity. It's measuring social debt: the cost of not knowing what Marcus knew.&lt;/p&gt;

&lt;p&gt;The reason this matters so much right now is that social debt compounds. Technical debt stays roughly stable until someone fixes it. Social debt gets worse every quarter as more of the people who held the context retire, leave, or simply forget. In a codebase with 30 years of history, the social debt can be catastrophic, and the technical debt, which is visible and tractable, is almost a distraction from it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Are Actually Inheriting
&lt;/h2&gt;

&lt;p&gt;When you take ownership of a legacy system, you are not inheriting software. You are inheriting a record of decisions made by people you will never meet, under constraints you may not be able to reconstruct, for reasons that may no longer apply.&lt;/p&gt;

&lt;p&gt;Some of those decisions will look bizarre. A field with twelve possible values where seven are actively used and five appear to be artifacts of a business process that was discontinued before anyone on the current team was hired. A module that is called from fourteen different places but only ever produces a meaningful result in three of them. A conditional branch that executes maybe once a year under a set of conditions that nobody has thought about since the compliance requirement it was written for changed in 2011.&lt;/p&gt;

&lt;p&gt;Every one of these is a decision. Someone made a choice. They had a reason. The reason mattered at the time. Whether it still matters is a question you cannot answer by reading the code alone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://arxiv.org/html/2410.21990" rel="noopener noreferrer"&gt;Studies estimate developers spend 58% to 70%&lt;/a&gt; of their time understanding existing source code, not writing new code. In a legacy system with poor documentation and high social debt, that number is almost certainly higher. The code is the most expensive reading material in the building, and a significant fraction of what you need to understand it isn't in the code at all.&lt;/p&gt;

&lt;p&gt;What you are inheriting, specifically, is this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The explicit layer.&lt;/strong&gt; What the code literally does: the data structures, the control flow, the integrations, the outputs. This is readable. It takes time, but you can get there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The implicit layer.&lt;/strong&gt; Why the code does it this way and not another way. This is where the real inheritance problem lives. The implicit layer contains the business rules that were never parameterized, the regulatory requirements that were encoded without comment, the performance constraints that shaped the architecture in ways that aren't obvious until you try to change something and something else breaks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The absent layer.&lt;/strong&gt; What the system used to do that it no longer does, but whose traces are still present in the code. Orphaned tables. Commented-out modules that were kept in the repository "just in case." Fields that are populated but never read. Conditions that handle states the system can no longer reach.&lt;/p&gt;

&lt;p&gt;Most inheritance failures happen because the new team focuses entirely on the explicit layer, has no systematic method for recovering the implicit layer, and doesn't know the absent layer exists at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Person-Shaped Holes
&lt;/h2&gt;

&lt;p&gt;There is a specific kind of problem that only manifests after someone senior leaves a team, and it is almost impossible to see coming from the outside.&lt;/p&gt;

&lt;p&gt;When a developer has been working on a system for years, they develop what researchers call a "mental model" of the system: a cognitive map of how the pieces fit together, what the edge cases are, which parts are fragile, what the system is really doing underneath the behavior the documentation describes. That mental model is not in any document. It cannot be fully transferred in a two-week handoff period. It lives in the person.&lt;/p&gt;

&lt;p&gt;When that person leaves, they take the mental model with them. What remains is a code system that looks the same on the outside but has a person-shaped hole in its surrounding knowledge.&lt;/p&gt;

&lt;p&gt;The problem becomes visible in specific ways. New changes cause unexpected regressions in parts of the system that seem unrelated. Support tickets start arriving about behaviors that were always the system's behavior, but that the previous team would have known not to change. Debugging sessions that would have taken ten minutes with the original developer take three days. Production incidents occur in exactly the systems where the most senior people left.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://arxiv.org/pdf/2401.03303" rel="noopener noreferrer"&gt;Bus factor research&lt;/a&gt; defines the minimum number of developers who would need to leave for a project to effectively stall. For most legacy enterprise systems, the meaningful bus factor is not calculated from commit history. It's calculated from who understood which undocumented system behaviors, and that information is rarely tracked anywhere.&lt;/p&gt;

&lt;p&gt;What makes this worse in the current moment: the COBOL and mainframe developer population is aging faster than any other segment of software engineering. The people who built systems that now process trillions of dollars in daily transactions are retiring at scale. The knowledge they hold is not in documentation. It is in decades of accumulated context that was never extracted because there was never a reason to extract it while they were still there. The reason only becomes clear after they're gone.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Do in the First Thirty Days
&lt;/h2&gt;

&lt;p&gt;Most advice about inheriting a codebase is technical: set up the dev environment, read the README, run the tests, find the CI pipeline. That advice is fine for greenfield projects or well-maintained modern systems. For a legacy codebase with genuine social debt, it is the wrong starting point.&lt;/p&gt;

&lt;p&gt;Here is what actually matters in the first thirty days.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Find the people first.&lt;/strong&gt; Your most important resource is not the repository. It is the people who can still answer questions about what the code was meant to do. That includes people still at the organization who worked on earlier versions, people who can introduce you to retirees or former employees who might take a call, and business-side stakeholders who have been using the system long enough to know when its behavior changed and what changed it. These conversations have a shelf life. Every month you wait, the knowledge degrades further.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Treat code as evidence.&lt;/strong&gt; The code is accurate about what the system does right now. It is not accurate about why, about what it used to do, or about what it was supposed to do. Approach it the way an archaeologist approaches an artifact: what can this tell me about the people and conditions that produced it? What questions does it raise that I need to answer from other sources?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Map the absent layer early.&lt;/strong&gt; Before you start optimizing or modernizing anything, do a pass specifically looking for what used to exist. Look for commented-out code that nobody has removed. Look for database tables that have no application code reading them. Look for fields that are written but never read, or read but only in conditions that can't currently be reached. This is not wasted time. Every one of these is a question you need to answer before you change anything nearby. Removing what looks like dead code without understanding why it exists is one of the most reliable ways to cause an incident eight months later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Document as you discover.&lt;/strong&gt; The instinct is to wait until you understand the system before writing anything down. That instinct is wrong. Document the questions as you encounter them. Document the partial answers you receive from conversations. Document the assumptions you are making and why. Your notes from the first thirty days, even if incomplete and sometimes wrong, are enormously valuable to the next person who inherits the system. You are not just learning; you are beginning the process of converting social debt back into explicit knowledge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resist the rewrite urge.&lt;/strong&gt; It will be strong. The code will look strange, inconsistent, and in places genuinely bad. Some of it is bad. But a significant fraction of what looks like bad code is code that is solving a problem you don't fully understand yet. The off-by-one workaround in module B that looks like a bug is compensating for the off-by-one behavior in module A that was never fixed because downstream systems adapted to it. Rewriting module B correctly will break those downstream systems. You won't know this until you do it. The best defense against this class of mistake is to build understanding before you build changes, even when the changes look obviously correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Documentation Nobody Writes
&lt;/h2&gt;

&lt;p&gt;There is a category of knowledge in every legacy system that is almost never documented, and the absence of it is responsible for a disproportionate share of the incidents, failed modernization projects, and inherited system nightmares that development teams live through.&lt;/p&gt;

&lt;p&gt;It is not the architecture. Architecture gets documented, at least partially. It is not the API surface. That gets documented too, eventually. It is not even the business rules, which people at least know they should document even when they don't.&lt;/p&gt;

&lt;p&gt;The undocumented category is the constraints: the things the system cannot do, the conditions under which it behaves unexpectedly, the input combinations that were never handled because they were never supposed to occur, the integration behaviors that depend on undocumented timing assumptions between components.&lt;/p&gt;

&lt;p&gt;A system's constraints are almost entirely absent from its documentation because constraints are invisible to the people who built the system. They know the constraints. The constraints are encoded in every decision they made. From the outside, the behavior just looks like behavior. The constraint that explains it is invisible unless you already know it's there.&lt;/p&gt;

&lt;p&gt;When you inherit a system, you discover constraints the hard way: by violating them. You change something that looks safe to change, and something else breaks in a way that looks unrelated and takes days to trace. You add a record that looks syntactically valid and the nightly batch job silently produces incorrect results for three months until a quarterly report catches the discrepancy.&lt;/p&gt;

&lt;p&gt;The discipline of constraint archaeology, which is what I would call the systematic effort to surface and document what a system cannot do before trying to change what it does, is not widely practiced. It is not a named methodology. There is no tool category for it. Most organizations skip it entirely, and then spend years recovering from the consequences.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Surviving This Actually Looks Like
&lt;/h2&gt;

&lt;p&gt;I want to be concrete about what it looks like when a team handles this well, because the successful cases are underreported.&lt;/p&gt;

&lt;p&gt;The pattern that works is not a grand knowledge-capture initiative. Those rarely succeed. By the time an organization decides to document its legacy systems comprehensively, the people who could have provided the most important context are already gone.&lt;/p&gt;

&lt;p&gt;The pattern that works is incremental, opportunistic, and attached to real work.&lt;/p&gt;

&lt;p&gt;Every time a developer touches a part of the system, they document what they learned. Not a full specification, just a note. "This field has twelve possible values. Seven are used in active code paths. Five appear to be artifacts of a product line that was discontinued. I don't know which five. Need to check with the billing team." That note, imperfect as it is, is worth more than no note. The next person to touch this code starts their investigation from a better position.&lt;/p&gt;

&lt;p&gt;Every time an incident occurs, the post-mortem documents not just what broke and how it was fixed, but what was learned about the system's behavior that wasn't known before. Incidents are expensive knowledge-generation events. The knowledge should be captured.&lt;/p&gt;

&lt;p&gt;Every time a senior developer leaves, their exit interview includes a structured conversation specifically about the system: what are the parts you're most worried about? What do you know about this codebase that isn't written down anywhere? What would you want the person who replaces you to understand before they touch certain modules? This is different from a standard handoff. It is explicitly asking the person to surface the implicit layer before they go.&lt;/p&gt;

&lt;p&gt;None of this is heroic or expensive. It is the practice of treating knowledge as an asset with the same seriousness you apply to code. A codebase without its surrounding knowledge is like a legal contract without the negotiating history: it says what it says, but understanding what it means requires context you no longer have.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Note on the Current Moment
&lt;/h2&gt;

&lt;p&gt;This problem is not getting better. It is getting worse faster.&lt;/p&gt;

&lt;p&gt;The combination of the retiring mainframe developer cohort, the acceleration of AI-assisted development which produces code faster than understanding can keep pace with, and the economic pressure to modernize legacy systems quickly is creating conditions where social debt is accumulating at a rate that technical tooling cannot address.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mobisoftinfotech.com/resources/blog/legacy-system-modernization-cost-strategy-roi" rel="noopener noreferrer"&gt;Industry research on legacy risk&lt;/a&gt; found that 42% of critical business logic in legacy systems is at risk when key personnel leave, because "the system is the documentation" in most legacy environments. That's not a prediction. That's a current condition. The business logic that runs large portions of global financial infrastructure, insurance systems, and government operations exists, right now, in a state where it can only be understood by people who are within a few years of retirement.&lt;/p&gt;

&lt;p&gt;The tools being deployed to accelerate legacy modernization are genuinely useful for the explicit layer. They are limited for the implicit layer and essentially blind to the absent layer. They can tell you what the code does. They cannot tell you what it was supposed to do when it was written, what it used to do before the 2009 changes, or which parts of its current behavior are intentional and which are workarounds for problems that no longer exist.&lt;/p&gt;

&lt;p&gt;This is not an argument against using those tools. It's an argument for sequencing the work correctly: understand before you modernize, not while you modernize, and not after.&lt;/p&gt;

&lt;h2&gt;
  
  
  What 27 Years Taught Me
&lt;/h2&gt;

&lt;p&gt;The developers who handle legacy inheritance best share a particular quality of mind. They are genuinely curious about the people who came before them. They approach the code with something closer to respect than contempt. Not because old code is inherently good, and a lot of it isn't, but because they understand that every line of it represents a decision made by a human being who was solving a real problem with the tools and knowledge they had at the time.&lt;/p&gt;

&lt;p&gt;That attitude is not just ethically appropriate. It is pragmatically correct. The developer who approaches a legacy codebase as a puzzle left by predecessors they want to understand will discover things the developer who approaches it as a mess to be cleaned up will miss. The missed things will cause incidents. The incidents will cause delays. The delays will cost far more than the time spent trying to understand the system before changing it.&lt;/p&gt;

&lt;p&gt;The hardest part of inheriting a legacy codebase is not the code. The hardest part is accepting that you can't fully understand it from the code alone, and doing the work, including the human work, to recover what isn't there.&lt;/p&gt;




&lt;p&gt;If you've inherited a legacy system: what was the thing that blindsided you most? Was it in the code, or in the context around it?&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>legacy</category>
      <category>code</category>
    </item>
    <item>
      <title>Your AI Search Is Only as Smart as What Your Codebase Forgot to Document</title>
      <dc:creator>Shlomo Friman</dc:creator>
      <pubDate>Fri, 08 May 2026 11:08:13 +0000</pubDate>
      <link>https://forem.com/shlomofr/your-ai-search-is-only-as-smart-as-what-your-codebase-forgot-to-document-584g</link>
      <guid>https://forem.com/shlomofr/your-ai-search-is-only-as-smart-as-what-your-codebase-forgot-to-document-584g</guid>
      <description>&lt;p&gt;Everyone deploying enterprise AI search is running into the same wall, and blaming the wrong thing.&lt;/p&gt;

&lt;p&gt;The model isn't the problem. The retrieval pipeline isn't the problem. The embedding strategy isn't the problem. The problem is what you're asking the AI to search through, and how much of the knowledge that actually runs your organization was never written down anywhere the AI can find it.&lt;/p&gt;

&lt;p&gt;It lives in the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Knowledge That Never Made It Into a Document
&lt;/h2&gt;

&lt;p&gt;When you deploy a RAG-based search layer over your enterprise systems, the standard assumption is that your knowledge lives somewhere retrievable: wikis, runbooks, Confluence pages, ticket histories, README files. The AI retrieves the relevant chunks, grounds its answers in them, and gives you something useful.&lt;/p&gt;

&lt;p&gt;That assumption holds for maybe 30% of the knowledge that actually matters in an enterprise system.&lt;/p&gt;

&lt;p&gt;The rest is implicit. It's baked into the logic of applications that have been running for a decade or more. It's in the field names that made sense in 2003 when the original team named them. It's in the hardcoded values that represent business rules nobody wrote a ticket for, because at the time there was no need: everyone knew. It's in the conditional branches that encode compliance requirements from a regulatory environment that has since changed, where the code was updated but the documentation wasn't.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.gosearch.ai/blog/why-ai-agents-underperform-rag-retrieval-gartner/" rel="noopener noreferrer"&gt;Gartner's 2025 Market Guide&lt;/a&gt; for Enterprise AI Search identified the knowledge layer, not the generation model, as the primary bottleneck in failing deployments. The most common failure mode isn't a bad LLM. It's a retrieval layer that can't surface what's needed because what's needed was never put anywhere retrievable.&lt;/p&gt;

&lt;p&gt;That's not a retrieval problem. It's a documentation problem that predates AI by twenty years.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "Undocumented" Actually Means in a Codebase
&lt;/h2&gt;

&lt;p&gt;When developers say a system is undocumented, they usually mean there are no comments, no architecture docs, no wikis. That's part of it.&lt;/p&gt;

&lt;p&gt;But the deeper problem is subtler. Even well-maintained systems have a class of knowledge that is structurally impossible to capture in conventional documentation: the knowledge that was so obvious at the time that nobody thought to write it down.&lt;/p&gt;

&lt;p&gt;Consider a few examples.&lt;/p&gt;

&lt;p&gt;A field called &lt;code&gt;ACCT-TYPE&lt;/code&gt; in a COBOL program has twelve possible values. Seven of them are used in active logic paths. The other five exist in the data but are never referenced by the application, because the business processes they represented were retired in 2009. An AI search tool has no way of knowing that. It sees twelve values. It doesn't know that five of them are artifacts of a world that no longer exists.&lt;/p&gt;

&lt;p&gt;A batch job runs nightly. Its processing sequence matters: module B must run after module A because module A writes an intermediate file that module B reads. That dependency is not in any document. It's in a JCL script that nobody has looked at in six years. If someone asks the AI "what does this system do overnight," the answer will be technically incomplete in ways that matter.&lt;/p&gt;

&lt;p&gt;A customer record field called &lt;code&gt;STATUS&lt;/code&gt; has a value of &lt;code&gt;7&lt;/code&gt;. What does &lt;code&gt;7&lt;/code&gt; mean? The answer is probably in the code that processes it. It might be in a comment. It might be in neither. It might be in the memory of someone who retired in 2017. The AI can retrieve the word "STATUS" from a dozen documents. It cannot tell you what &lt;code&gt;7&lt;/code&gt; means unless something, somewhere, says so.&lt;/p&gt;

&lt;p&gt;This is what &lt;a href="https://www.in-com.com/blog/how-cross-system-data-alignment-improves-data-consistency-across-enterprise-platforms/" rel="noopener noreferrer"&gt;cross-system data alignment&lt;/a&gt; breaks down to at its most fundamental level: different parts of the organization operating with divergent interpretations of the same data, because the shared context that would unify those interpretations was never codified.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Breaks AI Search Specifically
&lt;/h2&gt;

&lt;p&gt;Traditional keyword search fails at this problem gracefully. It doesn't know what it doesn't know, and it doesn't pretend to. You search for "STATUS field values," you get whatever documents mention those words, and you accept that you might need to dig further.&lt;/p&gt;

&lt;p&gt;AI search fails at this problem ungracefully. It generates confident, fluent answers from whatever it retrieves. If what it retrieves is incomplete, the answer is incomplete in a way that sounds complete. The system will tell you what &lt;code&gt;STATUS 7&lt;/code&gt; means if there is anything anywhere that describes it. If there isn't, it may interpolate from context, from similar patterns in other documents, from general knowledge about enterprise systems. The answer will sound plausible. It may be wrong.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@tobias_pfuetze/the-rag-renaissance-that-never-happened-b589199567c6" rel="noopener noreferrer"&gt;Industry surveys from 2025&lt;/a&gt; found that 72% of enterprise RAG implementations either failed outright or significantly underperformed in their first year. The most cited root cause was not model quality. It was data quality and retrieval relevance, which is a polite way of saying: the knowledge needed to answer the questions wasn't in the knowledge base.&lt;/p&gt;

&lt;p&gt;What nobody says clearly enough is that for enterprise systems with history, a large share of that knowledge was never in any document. It was in the people who built the system, and when they left, it went into the code. The code still runs. The explanation of why it works the way it does is gone.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Archaeology Step Nobody Is Doing
&lt;/h2&gt;

&lt;p&gt;There is a step that should come before any enterprise AI search deployment. Most organizations skip it entirely, because it is slow, unglamorous, and has no vendor selling it.&lt;/p&gt;

&lt;p&gt;That step is codebase archaeology: systematically reconstructing the implicit knowledge embedded in the applications themselves before building a retrieval layer on top of them.&lt;/p&gt;

&lt;p&gt;What does that look like in practice?&lt;/p&gt;

&lt;p&gt;It means tracing every field from definition through every place it is read, written, and transformed, building a map of what the data actually means in context. It means inventorying every hardcoded value and asking: is this a constant, or is this a business rule that was never parameterized? It means identifying every conditional branch where a missing &lt;code&gt;else&lt;/code&gt; clause represents an implicit assumption. It means mapping the dependencies between modules that exist in execution order but not in documentation.&lt;/p&gt;

&lt;p&gt;None of this is AI work. It is reading work. It requires treating the codebase as a primary source, the way a historian treats a primary source: not as executable instructions, but as evidence of decisions made by people who had context you no longer have.&lt;/p&gt;

&lt;p&gt;The output of that work is something that can actually go into a knowledge base. Field definitions with business context. Value code glossaries. Dependency maps. Process flows with the implicit steps made explicit. Annotated logic explanations for the branches that would otherwise be opaque.&lt;/p&gt;

&lt;p&gt;That is the knowledge layer an enterprise AI search needs to work. And in most organizations that have been running on the same core systems for ten or twenty years, most of it does not exist yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Compounding Problem of System Fragmentation
&lt;/h2&gt;

&lt;p&gt;The challenge gets harder as enterprise systems grow more fragmented. Most organizations operate not on a single legacy platform but on a constellation of systems that evolved independently and were later integrated through interfaces, APIs, and batch transfers that nobody fully documented either.&lt;/p&gt;

&lt;p&gt;Each of those systems has its own implicit knowledge layer. Each integration point between them represents an additional layer of context that may never have been written down: why this field maps to that field, why the transformation applies in this direction but not the other, why the timing of the transfer matters.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.ibm.com/new/announcements/new-enterprise-data-tools-for-enterprise-ai" rel="noopener noreferrer"&gt;IBM has estimated&lt;/a&gt; that 90% of data generated by enterprises is unstructured, but even that framing understates the problem for organizations with long-running legacy systems. It is not just that the data is unstructured. It is that significant portions of it are context-free: values and records whose meaning is only recoverable by reading the code that processes them.&lt;/p&gt;

&lt;p&gt;An AI search layer deployed over that environment is working with a fractured, context-stripped knowledge base. It can retrieve. It cannot understand, because the understanding was never externalized.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Do Before You Deploy
&lt;/h2&gt;

&lt;p&gt;None of this is an argument against enterprise AI search. The capability is real, and the demand is legitimate. Organizations need better ways to navigate the accumulated knowledge in their systems, and AI-assisted search can provide that.&lt;/p&gt;

&lt;p&gt;The argument is about sequencing.&lt;/p&gt;

&lt;p&gt;Before the retrieval layer, there needs to be a documentation layer. Before the documentation layer, there needs to be an extraction layer: a systematic effort to pull the implicit knowledge out of the codebase and make it explicit. That work requires tools that can parse code structure, &lt;a href="https://www.in-com.com/blog/research-execution-dependency-structure-tracing-data-flow-and-execution-paths/" rel="noopener noreferrer"&gt;trace execution paths&lt;/a&gt;, and surface the dependencies and logic patterns that human readers would miss in a manual pass.&lt;/p&gt;

&lt;p&gt;The organizations that will get durable value from enterprise AI search are the ones that treat this as a data preparation problem first. Not a model selection problem. Not a pipeline architecture problem. A knowledge extraction problem that starts with the oldest, least-documented, most business-critical systems in their portfolio.&lt;/p&gt;

&lt;p&gt;The AI can search what is there. The work is making sure what needs to be there, is.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>legacy</category>
      <category>data</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
