Internal publication

We've Seen This Before

Jonatan Wallgren · 25 September 2026

Full essay

A few days ago I read a complaint from a software engineer, shared on Reddit. They had joined a company where specifications, tickets, code, tests and reports were all being produced through Claude Code. There was barely time to read what it produced. Management apparently wanted to know why delivery was still slow. If generating code was no longer the bottleneck, what was taking so long?[1]

I have no way of knowing how accurately the account describes that company. But I recognised the pressure it described: keep delivering, deal with the consequences later. The software continues to work. The next request arrives. There is always a reason to make one more change before looking properly at what the previous changes have left behind.

We've seen this before.

Early in my career I worked somewhere that treated “quick and dirty” as a development philosophy. I also worked on betting products where some systems represented domain data in multidimensional arrays. To give an invented example, a particular selection in a football-pools game might have ended up behind something like `game[2][0][5]`.

The software worked. But when you needed to change its behaviour, you first had to recover what that expression meant and where else the same assumptions appeared. The route to the value was in the code. The meaning took more work.

I had come to Flash because it made substantial interactive browser clients practical for the work we were doing. Building the same kind of client with ordinary browser technology was difficult at the time. My next job used Flash for much the same reason. Once we were building applications like that, organising their behaviour and dependencies became a practical engineering problem.

I became seriously interested in application architecture through the Flash and ActionScript community. I was active on the Flashcoders mailing list, where people were already arguing about these problems. Parts of that community were teaching explicit application architecture by 2003–2004.[2] It was an important part of my education.

I had already used these ideas successfully when designing a frontend system. I separated responsibilities and controlled how data moved through it. These were choices I understood and had put into practice before the difficult maintenance period that followed.

Then, in 2007–2008, I spent much of my time maintaining substantial Flash clients where useful boundaries were badly lacking. I remember conditionals hundreds of lines long. Knowing why the structure was troublesome did not make the work easy. A symptom would lead to an apparent cause. Investigating it would expose a hidden dependency, and a local fix could have an unexpected consequence somewhere else.

That period deepened my engineering judgement. I had learned and used architectural ideas already. Maintenance made their consequences visceral: how much investigation a small request could require, and how difficult it could be to know what a change would affect.

These are my experiences. They explain why the complaint felt familiar. Software engineering has spent decades studying the broader problem: useful changes can leave a system harder to change again.

01

The Next Change Inherits the Last One

Imagine a subscription service. Initially, one part of the system decides whether a customer has access. Cancellation stops renewal, but access continues until the period already paid for has ended. The rest of the application asks for that decision.

Then a reporting job needs to count active customers. A support screen needs to explain why somebody still has access. A promotion introduces an exception. An integration needs to work without calling the original service. Each request makes sense. Each implementation can satisfy the people who asked for it. Gradually, several parts of the system come to carry their own version of what an active subscription means.

Now somebody asks for a pause button.

Before implementing it, you have to find those versions of the rule. You have to understand their differences and decide which differences still matter. Missing one can produce a bug. Finding all of them still takes work. The service may have been operating successfully throughout.

This is why the arrangement of software matters. A change to one business rule can become a change to many parts of a system. Parnas made the containment of changes central to his argument about how to divide programs into modules back in 1972.[3] Architecture affects how much of the system you must understand and coordinate before you can safely alter it.

Eick and colleagues later examined the change history of a successful telecommunications system that was already fifteen years old. It was still receiving features. They found signs that changes were spreading more widely through the code and that its modular structure was weakening. They did not find dramatic, widespread decay.[4]

That gradual result is what makes the study useful here. There was no spectacular collapse to explain. The product continued to deliver while some of the conditions for future work became less favourable. Success and deterioration could occupy the same system at the same time.

There is no guarantee that an organisation will notice this while it still has good choices available. Engineers may feel the extra work while management sees the features arriving. A difficult part of the system can be avoided for a while. More people can help keep delivery moving. Even when everybody knows that changes have become painful, they may disagree about why, or never find time to investigate.

And sometimes accepting the difficulty is sensible. If you have six months of runway and do not know whether anybody wants the product, building for ten years of maintenance may be bad engineering. A rough implementation can buy the information you need. The company might not survive. The feature might disappear. Replacement might be cheap.[5]

The bargain changes when the experiment succeeds. Customers start depending on it. The temporary integration becomes a permanent dependency. The component that was supposed to last six months is still there five years later.

The original decision may have been good. Whether it remains good is a new question.

Answering it requires remembering enough of the bargain to recognise that something has changed. What was temporary? What did we expect to replace? Which costs were we willing to accept while we learned? Continuing with the existing system might still be the right choice. So might repairing it, containing it or replacing it. The important thing is that the choice remains available for examination.

02

Agreement Can Be the Same Mistake Repeated

This is where the new tools make the old problem interesting. AI can help us make far more changes. It can also help us understand what those changes are doing. In some workflows it now produces much of the material through which we would expect to find out: the specification, the implementation, the tests, the explanation and the review.

Return to the subscription service. Suppose we ask an agent to implement cancellation. The request says customers should keep access until their paid period ends. Somewhere in turning that request into a specification, the rule becomes “cancellation ends access immediately”.

The code follows the specification. Generated tests check that access ends immediately. The documentation describes that behaviour. A reviewer compares the code with the specification and finds that it matches. Everything agrees, and the customer still loses access they have paid for.

The problem is easy to see when the original request is sitting beside the result. It becomes less obvious if each stage mainly consults what the previous stage produced. Agreement can then be the same mistake appearing several times.

We now have experimental evidence for a narrower version of this. A study of generated tests found that faulty implementation context could lead the tests to inherit mistakes from the code they were supposed to check.[6] That gives us a concrete reason to care where a test gets its expected answer. It does not establish that this process causes years of organisational drift.

A person reading the original request could catch our cancellation error. So could another invocation of the same model. A different model given only the mistaken specification might miss it. What matters is whether the checking process has something that can contradict the mistake. Merely asking for another opinion does not tell us that it has.

The cancellation error concerns behaviour. The scattered access rules concern the difficulty of later changes. They can occur separately: the wrong rule can be neatly implemented, and the right rule can be spread everywhere. My concern is what happens over time if a workflow preserves too little of the evidence needed to notice either problem.

Suppose today's implementation becomes tomorrow's explanation of how the system is supposed to work. That explanation then guides the next change. A temporary arrangement can gradually acquire the authority of a design decision, simply because later work keeps building on it. If changes arrive much faster, how well do we notice and revise those inherited assumptions?

I do not know the answer. The speed of production could make the problem worse. The speed of examination and repair could make it better. Both belong in the question.

I do recognise one way that work can stop making progress. I have repeatedly watched a coding agent make a fix, encounter a different error, add a patch and bring back an earlier problem. It develops a new explanation and changes something else. At some point I realise that the underlying problem is no longer becoming clearer. The files are changing, but the investigation has stopped moving forward.

Other experienced AI-assisted developers I have spoken with recognise this too. Those conversations are anecdotal, but researchers have also observed repeated unsuccessful approaches and repairs aimed at the wrong problem in agent runs.[7] Thrashing is a useful name for that failure to converge.

What we do not know is whether accumulated architectural deterioration makes it more likely. It is easy to imagine how hidden dependencies or misleading explanations could contribute. An agent can also struggle with a difficult task in well-organised software. My experience gives me a reason to ask about the connection; it cannot establish it.

03

A Software System Is Also Being Taken Somewhere

Each decision helps determine which future changes will be easy, which dependencies we accept and which options remain practical.

Some of that direction is visible in the architecture. Concepts are grouped together because we think they belong together. We put a boundary around a dependency because we may want to replace it. We accept an awkward arrangement because we expect it to be temporary. These choices express an understanding of the kind of system we are building.

That understanding should change. We learn what customers need. We discover that a boundary is in the wrong place. An experiment becomes a long-term commitment. A good engineering process must be able to change its mind without losing track of what it is reconsidering.

Imagine that we want to reduce our dependence on a cloud provider. A new managed service makes one feature much easier to build. Then another service solves another problem. Every addition works, passes its tests and is straightforward to maintain. After enough additions, leaving the provider has become a substantially larger undertaking.

We might decide that the benefits justify staying. That could be a good outcome. But we need some way of bringing those individual decisions back into contact with the aim they are changing. Otherwise we may discover much later that the choice has become more expensive than we realised. Nothing needs to break for that to matter.

This adds a question alongside the older concern about maintenance. Can this change be challenged by evidence? Can we recover the constraints and reasons relevant to it? And are the cumulative choices still serving goals we can recognise and revise?

04

What Needs to Survive

A permanent architect can provide continuity, but people forget, defend outdated choices and leave. An agent arriving for a single task might be able to retrieve history that no individual developer remembers.

During that 2007–2008 maintenance period, I became unusually careful about commit messages. Version-control history was often one of the few places I could look for an explanation of strange code. The diff showed what had changed. A message saying `committing fix` told me almost nothing about why. Whoever made the change may still have known something that I now had to struggle to recover.[8]

Recently, I took part in a mob-style technical interview with a software team. I remember being told that plans used to steer coding agents were committed to version control.[9] Thinking about that afterwards, I initially thought of a committed agent plan as a gigantic commit message.

A plan primarily describes what we intend to do. Even a detailed plan may say little about why. One sentence might preserve more: “We keep access decisions separate from payment-provider state because customers retain access until their paid period ends.”

Implementation can teach us something the plan did not anticipate. A migration might need a workaround until another system is upgraded. The final code shows the workaround; recording what we discovered could explain why it exists and when it can go.

In my earlier maintenance work, too little explanation often survived. AI can produce pages of plans while the reason for one important choice remains missing or hard to find.[10] What matters is preserving information that could change a future decision and would be difficult to reconstruct: perhaps a temporary compromise or the reason we rejected an alternative.[8]

The process still has to do something with what it remembers. A later person or agent needs to find the record, distinguish a proposal from an accepted or superseded decision, and challenge it against current evidence. AI could help connect the code to the relevant history, making the right record cheaper to find.[10] Retrieving it accurately would not make it current.

Continuity can live in the process around the work: accessible goals and reasons, constraints enforced by checks, and feedback that prompts reconsideration.[11] Each agent invocation can end while that process continues, preserving enough for the next decision and for recognising when yesterday’s reasons no longer apply.

05

The Cost of Understanding Can Fall Too

A developer may truthfully say, “I just tell the agent what I want,” while working inside an environment that supplies a great deal of direction already. Framework conventions and existing architecture suggest patterns; types, compiler checks, database constraints, tests and deployment checks reject some mistakes. Short prompts do not imply an unsteered process.

AI may also make understanding and repair cheaper, helping recover architecture, map dependencies, retrieve history, identify conflicts and refactor code.[12] I would have welcomed tools that could recover meaning and dependencies from the systems I maintained. The fact that the work taught me something does not make its cost desirable.

If AI-heavy systems remain reliably cheap to change over years, even as individual humans remember less of them, that would weaken the concern. If AI-assisted repair keeps total maintenance costs falling, structural deterioration may matter less than historical experience suggests.

The people in the opening account felt they had lost the time to examine what they were shipping. Maintenance taught me how much earlier decisions can make a small request cost. Continued delivery alone will not tell us what those decisions are leaving behind.

I want to know what we will inherit from all this activity. Later people and tools will need to make reliable changes without first reconstructing an increasingly tangled world. They may also want to reconsider what the system is becoming. What interests me is whether they can still recover the choices that brought it there, judge which ones still serve them, and take it somewhere else.

Sources

  1. Account shared on r/ClaudeAI, reproducing a post attributed to voxium (@v0xium), dated 20 September 2026. The workplace account is unverified.
  2. Contemporary ARP architecture material from December 2003 and Colin Moock’s Essential ActionScript 2.0 (2004) document explicit application architecture in parts of the Flash/ActionScript community. This establishes the availability of teaching and practice, not their prevalence across the ecosystem or priority over other platforms. The author’s participation, learning, designs and maintenance experiences are personal testimony; his earlier architecture was not available for independent inspection.
  3. Parnas (1972), On the Criteria To Be Used in Decomposing Systems into Modules. The subscription service is an invented example. Parnas supplies the design rationale for containing the effects of change, not a measurement of this hypothetical case.
  4. Eick et al. (2001), Does Code Decay? Assessing the Evidence from Change Management Data. The study found increasing change span and declining modularity in a successful fifteen-year-old telecommunications system, but no dramatic widespread decay. Feature-effort analysis used 54 features and was explicitly suggestive. Related research includes Xiao et al. (2016), Identifying and Quantifying Architectural Debt, and MacCormack and Sturtevant (2016), Technical Debt and System Architecture. Their churn and cost measures are proxies or allocations, rather than direct measurements of every maintenance task’s labour; they do not establish a universal growth law.
  5. Cunningham (1992), The WyCash Portfolio Management System, and Besker et al.’s startup technical-debt study (2018). The startup study interviewed sixteen people in seven companies. Expediency depends on the business context; six months of runway is an illustration, not a finding or prescribed threshold.
  6. Konstantinou, Tambon and Papadakis (July 2026), On the Risk of Coding Before Testing: An Empirical Study on LLM-Based Test Generation Workflow. This preprint used controlled Python benchmark workflows. Faulty implementation context biased generated assertions and reduced fault detection compared with independently generated tests. Refinement addressed compilation problems rather than requiring tests to pass faulty implementations. This is evidence for a particular propagation mechanism, not for accumulated organisational deterioration.
  7. A Process-Centric Analysis of Agentic Software Systems, Beyond Resolution Rates, and Failure as a Process. These preprints analyse different datasets and definitions; their results are not a pooled prevalence estimate. They do not establish that accumulated architectural erosion caused the observed failures. The author’s experiences and conversations are personal testimony.
  8. The maintenance and commit-message account is personal testimony. Al Safwan, Elarnaoty and Servant (2022), Developers’ Need for the Rationale of Code Commits, used 20 interviews and two surveys of 26 respondents each to examine developers’ needs for goals, constraints, alternatives and other rationale beyond the diff. These are reported needs and recovery difficulties, not a measured return on documentation. Falessi et al. (2013), The Value of Design Rationale Information, reports two controlled experiments with 75 master’s students in which the value of information varied by task. Preserving consequential information that is difficult to reconstruct is the essay’s synthesis, not a validated universal rule or documentation format.
  9. The mob-style interview is the author’s direct experience. The practice of committing agent plans remains his recollection of what he was told; it has not been independently verified publicly. The account supplies an observation about a possible practice, not evidence of its effect on long-term maintenance.
  10. Codoban et al. (2015), Software History Under the Lens, examined history use through 14 interviews and a survey of 217 developers, identifying problems with volume, fragmented records and missing context. Fan et al. (2026), VibeMemBench, an arXiv preprint, studies coding-agent memory on 111 selected targets from 90 repositories. Targets were selected for benefiting from supplied experience in a reference setting. Directly supplying selected experience produced positive resolution differences on four of five held-out solvers, but every reported resolution-effect confidence interval crossed zero. Under the evaluated integration, four memory systems produced lower observed resolution than memory off in 11 of 12 solver–system combinations; none established superiority by its reported interval. This supports the distinction between stored history and usable context for later coding work, not a finding about long-term organisational memory or recovery of original intent.
  11. Nygard (2011), Documenting Architecture Decisions, describes retaining superseded decisions and their context. Leith (2026), Between the Commits, an arXiv preprint, examines one Python reverse-engineering project developed across Claude sessions over weeks under continuing human direction. Its archive contains handoffs written in one session and read in another, and an earlier plan reassessed against newer code and documentation. The human continued to supply goals, select and coordinate work, evaluate proposals and authorise commits. Raw-session coverage is incomplete. The case illustrates episodic agents participating in a continuing engineering process; it does not establish autonomous stewardship or long-term conceptual integrity.
  12. Recovering Software Architecture Intent from Historical Work Items Using Generative AI, Evaluating Large Language Models for Detecting Architectural Decision Violations, and Agentic Refactoring. These three preprints examine bounded maintenance tasks. Recovery was studied in two industry projects with practitioner evaluation. Conformance results were stronger for explicit, code-visible decisions. The refactoring study was observational and reported modest structural improvements, with human–AI attribution limitations. These results support useful components of maintenance, rather than a measured long-term reduction in total maintenance cost. As separate evidence about later maintenance, Borg et al. (2026), Echoes of AI. A preregistered, two-phase study involving 151 participants, predominantly professionals, found no significant downstream maintenance time or quality penalty in its bounded setting. A non-significant result is not proof of equivalence or a multi-year outcome.