Landed your first job? Burn your old CS homework.
Why your best solution as a student won’t make it in the real-world — and that’s okay.
This blog was originally published on Substack. Subscribe to ‘Letters to New Coders’ to receive free weekly posts.
When I started my first job as a professional software developer, I was shocked to find that most of the best practices I had learned as a student no longer applied.
You can be the best in your class. You can have an amazing, elegant solution to any problem school throws at you. And yet, once you enter the workforce, you might be in for a rude awakening.
Why? There’s a big difference between academic and practical solutions.
As a student, many of the problems you encounter in your coursework will have clearly defined starting and ending points. You will be learning concepts in an academic setting — often outside of their real-world context — building self-contained toy problems that don’t resemble the complex applications you will be working on as a pro.
On the job, the problems you will encounter are messy. You will rarely (if ever) build an entire application from start to finish yourself.
That said, while real-world problems are a lot more complex, you can still find a path through them. Today, I’ll show you how to problem-solve like a professional, so you can hit the ground running in your first job.
Let’s dive in!
The limits of academic solutions
Imagine that while you learned to code, you practiced each new concept by adding upon the same program you built for your very first coding project. This approach would lead to a far more complex program than starting from scratch. Now imagine that very first program codebase was written by 300 developers over the course of 20 years… Welcome to the real world.
Whether in a university program or a tutorial, academic solutions and their corresponding problems are designed purely for learning. No matter how elegant it may be, you can’t usually bring your solution from a student project to your first developer job.
This is because academic environments offer a blank slate, unlike real-world work environments. Blank slates are excellent for learning. They give us opportunities to apply our learnings by building projects from scratch. But once we enter the real world, the best practices and techniques that worked on our blank slates may not be relevant for our new situations. This is because we now have to work around existing code and its bugs or constraints.
At work, you’ll be dealing with codebases that could be decades old. The old code (i.e. “legacy code”) can be completely incompatible with what would otherwise be an academically sound solution.
When working with an existing program, you’ll have to make compromises between your ideal state and the existing codebase. For example, let’s say you’re writing a program that can store someone’s name. You have names with different characters and accents (e.g. é). Instead of removing the accents, you might want to keep them in their correct form. You could do this by creating a unicode string, which can support names with various characters. However, the unicode standard was not supported in many languages two decades ago. If you were to try to store these names in an older program, and an older part of the program tried to parse it, it would yield unintended results. It would be safer to opt for non-accented characters instead.
The difference between an academic solution and a workplace solution is like the difference between building a new house and renovating an old house. When building from scratch, you have the freedom to apply any practices you’d like. But an existing building has its own constraints and history you’d have to work around.
This may look like a disastrous renovation, but trust me, some of the solutions you’ll have to implement in legacy code will seem just as questionable!
Even if you wanted to add the most efficient technologies to an old building, it might have constraints that could make that difficult, impossible, or even disastrous. For example, if you wanted to install a modern HVAC system, an old building may not have the necessary electrical infrastructure and structural support. Worse yet, its walls and layout could be unable to support the weight or configuration of such a system. Installing a modern HVAC without considering these constraints could lead to inefficient heating and cooling or, at worst, structural damage.
Applying best practices to existing codebases is similar. Even if the engineering community has generally understood a best practice or new technique to be beneficial, their implementation in a legacy codebase could yield unpleasant results.
For instance, you can’t just decide to use modular architecture when your codebase is a monolith. Similarly, implementing new, touted security protocols could result in vulnerabilities if you don’t understand the legacy code’s dependencies.
This all being said, it’s not the case that every coding best practice can be risky in a codebase. Some practices are certainly safe to apply regardless of the legacy code you’re working with, such as code commenting or the DRY principle (Don’t Repeat Yourself).
Learning to work with legacy code
If legacy code is what’s preventing us from using more efficient solutions, why not just overhaul it and start over? Rewriting may seem like an obvious solution, but it’s not practical.
When I was working at Microsoft, the codebase we were dealing with was written using C99, a standard that was created when I was in high school. It was 15-year-old code, and there was so much in the codebase that upgrading all of it would have been difficult… so we just continued to build the software in C99. (There were other teams at Microsoft with legacy that was even older than this code, and they had to stick with it, too).
It may seem like a worthwhile investment to just start over and rewrite code. Truthfully, it isn’t.
For one, it’s incredibly costly. The development time and costs that would be involved would be immense. Meanwhile, services can’t afford to cease running while we rebuild them. There are still users who need them, and bugs that pop up everyday. You would need to hire an entire development to rewrite the software in parallel to the team that maintains it.
If development teams are expensive, we might have an urge to simply write a converter and just convert and upgrade everything. But you don’t know what unintended consequences changes can have. These kinds of mega upgrades will often uncover hidden bugs that were there for decades, but for some reason didn’t manifest themselves before they were changed. This alone holds us back. It would create a lot of problems for customers and developers alike.
There’s unimaginable complexity that comes with attempting to rewrite codebases, and the scale of the code that would have to be rewritten can be immense. In most cases, it’s not worth taking the risk to take on such an endeavor. As a developer working in a codebase, this means you have to learn to live with the decisions someone has already made — sometimes decades ago.
As always, success comes down to problem-solving
How do you make strong choices and smart moves in an imperfect world? Consider the nuances, and let your problem-solving skills do the rest.
After all, it’s not the specific coding skills you learn as a student that will serve you most in your career — it’s the problem-solving skills you build.
When solving problems, you are always dealing with constraints. Legacy codebases will give you various constraints themselves. From your first job onward, you’ll have to consider that code’s constraints and requirements as you consider optimal solutions. As a student, you may have jumped to a textbook example of a best practice… but as a professional, your solution may instead be a more creative compromise between the goal you want to achieve, and the legacy code you’re striving to maintain.
As a reminder, you can build your programming career from the basics on up with our Learn to Code courses and projects at Educative. These hands-on courses are designed to help you build and practice the real-world problem solving skills that will enable you to thrive as a professional developer.
Happy learning!
– Fahim