Open a file that's been in production for five years. The naming conventions shift halfway through. A function signature screams 2018 Angular. Below it, a React hook that arrived during the 2021 migration. Further down, server components bolted on last quarter. Three eras of the same team's thinking, stacked on top of each other, none fully erased.

You've been told this is a problem. Technical debt. Something to pay off. Clean it up, modernise it, get it under control.

A medieval scribe would recognise what you're looking at. They called it a palimpsest. Parchment was expensive, so when a text outlived its purpose, you scraped the ink off and wrote something new over it. But scraping is imperfect. The old text bleeds through. Some of the most important surviving works, lost Archimedes, lost Cicero, were found hiding underneath medieval prayer books. The monks thought they were writing on a blank page. They weren't. Nobody ever is.

git blame is paleography

Every naming convention, every architectural choice, every "weird routing thing from 2018" is a layer someone wrote on purpose. git blame lets you hold the parchment up to the light. You're reading through strata of decisions, each one made by someone who had a reason, even if that reason expired three framework versions ago.

The usual instinct is to scrape it clean. Rewrite. Start fresh. Treat the old text as noise. But the palimpsest teaches something different: the interaction between layers carries information that no single layer contains.

That Angular function signature tells you the team was building something ambitious in 2018. The React migration tells you they hit a wall. The server components tell you they're still investing. The sequence is the story. You can't understand the current layer without reading the ones underneath.

This is why you map the layers before you touch them. Not because documentation is virtuous, but because you can't rewrite a palimpsest you haven't read. The 2018 routing hack that looks like debt might be the only thing holding two services together. The inconsistent naming might mark the exact boundary between two teams' ownership. These aren't bugs. They're stratigraphy.

When to scrape the parchment

The palimpsest metaphor has a failure mode. It can become an excuse for never letting go. "We can't touch that, it's historical." Sometimes layers accumulate into illegibility. A codebase nobody can read serves nobody, regardless of how interesting its archaeology is.

Some palimpsests aren't worth preserving. When every change risks something nobody understands, and the cost of reading exceeds the cost of rewriting, you scrape. AI is shifting this calculus fast. A full rewrite that took months now takes weeks. The bar for "worth reading" versus "worth rewriting" has moved.

But even then, you read first. The rewrite that ignores its predecessor's lessons will recreate its predecessor's problems. You scrape the parchment because you read the old text, not instead of reading it.

The working surface

The teams that struggle aren't the ones with messy codebases. Every codebase with history is messy. The ones that struggle treat their codebase as either a clean slate or a museum.

A clean slate pretends the layers don't exist. A museum pretends every layer is sacred. A palimpsest is neither. It's a working surface. You read the old text. You write the new one. The bleed-through isn't contamination. It's context.

The question isn't whether your codebase has layers. It does. The question is whether anyone on your team can still read them.

Follow our bi-weekly SaaS show

Fast, honest insights from the trenches of SaaS. Andreas and Sjimi, partners at madewithlove, share what they’re seeing inside real SaaS teams and products every two weeks.