You've been given the task of implementing a new feature on an old codebase, but the code looks awful. How can you understand it as quickly as possible? Here are several shortcuts to help learn the important parts of new code without getting lost in the irrelevant details.
As programmers, we often have to join new projects, and the quality of the code can be all over the place. Even with an organized team, keeping code quality consistent throughout a medium-size-to-large project is a challenge.
That's why understanding poor code quickly can be a valuable skill to have. It can help you become very productive in a short time and reduce the stress that usually comes with being the new guy and having to play catch-up. Being in a conversation with a co-worker and not knowing what that person is talking about half the time is a terrible feeling.
On the other hand, this is a prime opportunity for showing your client or boss your worth and that you can get up to speed quickly and impress them. Most developers take weeks to months to become really productive with a codebase they did not build themselves.
Here's how to master awful code quickly.
Ask for help. It's the most efficient strategy
Other people have already learned how the code works, so why not ask them about it? You might think it makes you look like the newbie, but showing curiosity can have a strong impact on your image as an employee. If your boss's expectation was for you to get productive fast without asking questions, that's a misjudgment on her part.
Everyone takes time to get up to speed. Ask questions, and you'll impress the people who have the right attitude for teamwork.
In many cases, the original developers will have made strange or unexpected decisions, and that's where talking about the code will be much more valuable than reading it. This is even more the case if the documentation is lacking. Remember, existing developers have valuable project knowledge that is not written anywhere.
Make a list of concepts that don't make sense to you
There might be business concepts that are new to you or that are overly complex. It's important to get clarification about them before trying to work on code that handles those concepts, to avoid misunderstandings that might take a while to figure out.
It might even be the case that those ideas are mislabeled or represented in an unexpected way in a database. So avoid getting stressed over wrapping your brain around that, and just go to the source and ask your co-workers about it.
In the same vein, there might be modules, classes, or entities that don't have appropriate names. Make a note of them. Poorly named elements can lead to great confusion, so document them early, as well as anything else that will negatively affect your ability to think about how the code works.
Make it easy to reproduce bugs
By adding code versioning and a virtual machine such as Docker or Vagrant, you will greatly reduce the time it takes to reproduce a bug and to test your work on a new feature.
Any kind of misunderstanding about how the code works could lead you down a path of building the wrong thing, either because what you're building might already be there and you haven't seen it, or because things just don't work the way you thought.
At this point, you want to have Git version control in your project. That way you'll be able to go back to a stable release, or even just work on separate branches that can be discarded if needed.
It's even possible to gain greater reproducibility with Git, since you can use the stash to add testing or debugging code while you dig into a hard-to-track problem.
To learn about your project's architecture, take on configuration and documentation tasks early on.
The same can be said about VMs and reproducibility. They've become ubiquitous for any modern development team, but you will certainly run into projects that are not using them or even ready to run inside one. Sometimes your first step as a new team member is to document the steps you took to get an environment working, and eventually turn those steps into a VM setup script.
Build a diagram of components
A mind map of business concepts, an entity-relational diagram (ERD) of the database tables, and a simple diagram of code components can greatly help to reduce the pain of understanding new code. Don't remember how something works? Keep that ERD open.
In fact, any graphical tool that helps you digest information quickly and have a ten-thousand-foot view of a project will be valuable. Other examples of tools that could help you there are dependency graphs, logs, and a map of the project's technology stack.
One of the greatest time consumers in development is the integration point between systems. Having a global view of the project landscape will help you identify which integration points are interesting to examine. Those are the spots that have the most work put into them, and the most bugs.
On the other hand, technology moves fast, and there could be opportunities to replace large parts of the codebase with more modern and properly integrated solutions. An old framework might have developed a new and official way to solve a problem, or an entirely new library could have been developed with better compatibility in mind.
Use visualization and modeling tools to view the big picture.
Prepare for automated testing
Before you start breaking things, it's always prudent to add relevant unit tests. The problem with testing and poor code is that poor code is usually tightly coupled and hard (if not impossible) to test. You can't isolate components that are intertwined and indivisible.
In those cases, take a step back and test from farther away. Usually that means doing integration testing, for which there are many tools available. Web apps can be tested against HTTP requests, so it is at least possible to verify that the system will respond in the same way to the same inputs.
Integration testing has much worse performance than unit tests, though. Whenever you can, implement unit tests so you can have faster feedback on code changes. If that's not possible, then opt for functional or even integration testing.
This step should shed some light on the parts of the code that need work. If there's a large amount of tightly coupled code, that's a good target for refactoring after integration tests are done, and then for unit testing later.
Identify unusual or inadequate coding strategies
It's time to start doing some refactoring, but where do you start?
Usually, the best place is where things look weird, or where development best practices have not been followed. For web development, that could mean fat controllers with tightly coupled model code.
Keep in mind that the same strategies might be in use elsewhere. So if, for example, you identify that fat controllers are present, then it's likely that previous developers did not attempt to have thin controllers. You can expect to see the same problem in other controllers as well, since that reflected the style or shortcomings of the development process before now.
At first, work on a small task
Fixing a small bug on a feature that is conceptually simple will be very enlightening and will help you feel productive from the start.
This is a similar idea to what Andy Hunt and Dave Thomas call "tracer bullets" in The Pragmatic Programmer. The underlying logic is the same: Work on something end-to-end to prove to yourself that it's possible, and then progressively improve on that initial work.
The "tracer bullet" approach. Credit: The Pragmatic Programmer
A good example of the kind of simple improvement you can make is to take small refactoring steps with simple code. Identify a common programming practice that is not being followed, and apply it.
One of my favorites for this is un-nesting conditional blocks. So instead of having several if-if-if blocks, one inside the other, negate the first one and return, and do the same for all validation-type checks you can find. This can be as simple as inverting an "if" check and moving some code around, so the risk is almost non-existent and the flat code will be easier to read.
Be sure to work first on refactoring features that are easy to test, because even though the changes are low-risk, it's always a good idea to be able to validate your code before sending it to production.
Get on familiar ground before tackling critical code
Always learn how the project is set up first, and only then dive into the architectural side. The most critical pieces of business and integration code are the hardest to understand and modify. Avoid getting into trouble early on.
As a general rule, avoid business issues that would require you to know more than you currently do about the project, or about the business side. That usually means to stay away from transactional, payments or math-heavy code until it's starting to become familiar ground.
Once you are productive, are comfortable with the coding style for the project, and have no trouble fixing simple issues, it's time to work on the harder stuff—but not before.
Even if there is some urgency for fixing payment issues, for example, that kind of task can be incredibly risky. A mistake there could cost the project dearly, and that will be on you as well. Absolutely refuse to work on high-risk tasks early on if at all possible.
How to deal with an unfamiliar tech stack
For the last point, a common problem I've run into is that every new project usually includes some technology that I'm not familiar with.
When that happens, I have a couple of strategies that help me get up to speed fast. The obvious path for learning is reading documentation and getting familiar with the new technology. But while you will learn a lot about structure, that path will not help you learn the corner cases, which typically come with experience. ("Corner case" is an engineering term that refers to a situation that occurs outside of normal operating parameters.)
One way to speed up this process of gaining experience is to take question-based resources such as Stack Overflow and Quora and just read through them like a book. Find the most popular questions about the library you're learning and see what kind of problems other people typically run into. You're not going to necessarily run into them yourself, but just knowing what's possible is like shining a bright light onto your mind map of those new ideas.
Leverage Stack Overflow questions to gain experience fast. Credit: Stack Overflow
For a more targeted approach, find information about library features being used in your new project specifically. Look into those that are new to you and learn them ahead of time, before you have to touch that code at all.
Share your ideas
Learning new and complex things is not easy, and everyone develops their own strategies. These are mine, but I'd love to hear about your own approaches that were found through experience. Please share them in the comments field below.
Keep learning
Take a deep dive into the state of quality with TechBeacon's Guide. Plus: Download the free World Quality Report 2022-23.
Put performance engineering into practice with these top 10 performance engineering techniques that work.
Find to tools you need with TechBeacon's Buyer's Guide for Selecting Software Test Automation Tools.
Discover best practices for reducing software defects with TechBeacon's Guide.
- Take your testing career to the next level. TechBeacon's Careers Topic Center provides expert advice to prepare you for your next move.