Learning a new programming language is rarely just about learning new syntax.
That came clear to me when I recently had to work on a project written in Prel. I came into it with most of my experience in Python, where I was comfortable with the language, its conventions and also the way problems are typically structured. Perl was quite unfamiliar territory, I have seen some code in the past but never really embraced and worked with it. So the biggest challenge was not simply that the language looked different.
I inherited a quite difficult and legacy codebase and at first sight, my instinct was to approach the project the same way I would approach a new Python task: identify the part that needs to be changed, understand the immediate inputs and outputs and then start improving the implementation. That approach works well when the system is familiar and the code follows conventions you already understand.
In this case, it was totally the wrong starting point.
The discomfort of unfamiliar code
Perl has a reputation for being expressive, flexible and sometimes difficult to read. In a legacy system, all three qualities can appear at once.
The code contained some unfamiliar syntax, implicit behavior, compact expressions, global state and assumptions that were not documented anywhere. Variables seemed to change meaning depending on where they were used. Subroutines had side effects that were not obvious from their names. Some parts looked unnecessarily complicated, while others appeared simple but were connected to behavior elsewhere in the application.
My initial reaction was to translate everything mentally to a language that I am comfortable with, like Python.
I kept asking myself how I would write the same logic using dictionaries, functions, classes, exceptions or some clearer control flow. That helped me understand individual lines but it also created a dangerous bias. I started judging the Perl code according to Pythonic ways of working before I fully understood why it had been written that way.
Different languages encourage different styles. More importantly, old systems develop their own internal logic over time. A piece of code may look strange because it is poorly written but it may also look strange because it protects against a production issue that occurred years ago.
Without context, the two situations can become difficult to distinguish from one another.
Reading before changing
I believe that the most important lesson from the project was very simple. Read the code before trying to change the code. It can sound very obvious but in practice it requires discipline.
Reading the code is not the same as scanning until you find the line you want to edit. It means tracing the data through the system, identifying where the values originate, understanding which subroutines that mutate state, and also looking for dependencies that are not visible from the immediate call path.
I had to slow down in my work. Instead of immediately rewriting unfamiliar sections, I started documenting what I could prove about the system. I followed variables from input to output. I searched every place a subroutine was called. I compared similar code paths. I inspected logs and existing tests. Where tests did not exist, I created small experiments to verify how the code behaved.
The goal was not to understand all of Perl. It was to understand this particular system well enough to make a safe change. That shift made a significant difference.
Once I stopped treating the codebase as something that needed to be cleaned up immediately, I began to see its structure. Patterns started emerging. Some confusing decisions were still questionable however, but others turned out to be deliberate. Code that initially looked redundant was handling edge cases. Values that appeared unused were consumed indirectly. Certain conventions were inconsistent by modern standards but consistent within the application itself.
The code became less mysterious once I learned to observe it on its own terms.
Legacy code might be a communication problem
Working on this project reminded me that working with legacy code is not only a technical problem. It is a communication problem across time.
The original developers made decisions based on requirements, constraints, incidents and some deadlines that I could no longer see. The code was their remaining explanation, even though it was incomplete and missing documentation. My job was to reconstruct enough of that missing context to change the system in a responsible manner.
It is easy to enter an unfamiliar codebase and immediately notice everything I myself would do differently. It is much hared to recognize that our preferred solutions is shaped by our own experience, tools and assumptions.
Modernizing code can be valuable. Refactoring can reduce risk and make future work on the project easier. But changing code before understanding its behavior can turn an ugly but functioning system into a clean looking failure.
I believe that a few questions that will follow along to the next legacy project I work with is:
- What does the code currently do?
- Why might it have been written this way?
- What is the smallest safe change that solves the actual problem?
Only after answering those questions should any writing begin.
Growth that happens outside of the comfort zone
Most developers have a language in which they feel at home. For me, that language is Python. Its readability, ecosystem and overall development experience align well with how I like to solve my problems. But comfort can also create blind spots.
Working in Perl forced me to operate without many of my usual instincts. I had to learn new conventions, question familiar assumptions and last but not least, accept that my first interpretation of the code was often incomplete. That discomfort was valuable.
Learning a new programming language is not only about adding another technology to the CV. It is an opportunity to examine the habits we have developed in our primary language. It shows us which ideas a fundamental and which are simply conventions we have become accustomed to.
The Perl project did not make me abandon my preference in Python. It made me a better developer in general by strengthening some of my weaknesses. It is hard to change both familiar and unfamiliar code unless you can prove that you understand it.