As a software engineer, it is inevitable that we will have to change someone else’s code or add new features to their code. We are not familiar with the code, and it may not be relevant to the parts of the system we wrote. While this can be difficult and frustrating, being flexible enough to write code with other developers can be rewarding. These include increased impact, fixing broken software, and learning parts of the system that were previously unknown (as well as techniques and tricks from other programmers).


The book

Given that working in other developers’ code can be both frustrating and rewarding, it’s important to watch out for a few things that can go horribly wrong:

  • Our sense of self: We may think we’re the most competent, but we usually aren’t. We don’t know much about the code we’re changing. We don’t know the intent of the original author, what factors led to the code over many years, and what tools and frameworks the author used to write it. Humility is worth a million dollars, and we should always keep it.

  • Self-awareness of the original author: The code we’re working with is from another developer who has his/her own network, constraints, deadlines, etc., and of course his/her own life (which takes up a bit of work time). He/she is also a human being who is naturally defensive when we question a decision he/she has made or ask why the code is so bad. We should try to get the authors to work with us, not get in the way of our work.

  • Fear of the unknown: Many times we are exposed to code that we know little or nothing about. It seems like a terrible thing: we’re responsible for the changes we make, but it’s like walking around in a dark room with no light. Instead of being afraid, we should set up a framework in which we can safely make small and large changes while ensuring that we don’t break existing features.


The book

All developers, including ourselves, are human. So working on code written by someone else is human. In this article, we’ll look at five ways to leverage the best of humanity to get the most out of existing code and original authors, and improve the state of your code. While this list is by no means exhaustive, applying these methods will ensure that after we have made changes to someone else’s code, we can be confident that existing features will work as well as that new features will blend into existing code.


The book

1. Make sure there are tests

Does it work as expected for features written by other developers? Did the changes we made prevent it from working as expected? The only way to do this with confidence is to support the code with tests. When we read someone else’s code, there are two possible states :(1) there is no adequate level of testing, or (2) there is adequate level of testing. For the former, we’re stuck creating tests; For the latter, we can use existing tests to make sure our changes don’t crack the original code, and learn a lot about the intent of the code from the tests.


The book

Other Translations (1)

Creating a new test

This may sound sad: When we change another developer’s code, we are responsible for our actions, but we have no guarantee that the change will cause damage. Teasing is useless. No matter what state we find the code in, if we move the code, we are responsible for it. Therefore, we should control our behavior when we modify the code. If you don’t want to cause havoc, write your own tests.

It’s boring, but you can write tests to learn about the code, which is its main advantage. If the current code works well, we need to write tests that produce the expected output with the expected input. As we write tests, we learn more about the intent and functionality of the code. For example, the following code exists

public class Person {

    private int age;
    private double salary;

    public Person(int age, double salary) {
        this.age = age;
        this.salary = salary;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getAge() {
        return age;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public double getSalary() {
        return salary;
    }
}

public class SuccessfulFilter implements Predicate<Person> {

    @Override
    public boolean test(Person person) {
        return person.getAge() < 30 && 
            ((((person.getSalary() - (250 * 12)) - 1500) * 0.94) > 60000);
    }
}Copy the code

We don’t know anything about the functionality or the magic numbers used in the code, but we can create a set of tests that produce a known output from a known input. For example, a simple mathematical analysis of successful people’s salaries. We found that people under 30 are considered successful (by the code’s standards) if they earn about $68,330 a year. While we don’t know what those magic numbers mean, we do know they reduce the original salary. Thus, the $68,330 threshold is the base salary before deductions. Using this information, we can create some simple tests as follows:

Public class successtest {private static final double THRESHOLD_NET_SALARY = 68330.0; @Test public void under30AndNettingThresholdEnsureSuccessful() { Person person = new Person(29, THRESHOLD_NET_SALARY); Assert.assertTrue(new SuccessfulFilter().test(person)); } @Test public void exactly30AndNettingThresholdEnsureUnsuccessful() { Person person = new Person(30, THRESHOLD_NET_SALARY); Assert.assertFalse(new SuccessfulFilter().test(person)); } @Test public void under30AndNettingLessThanThresholdEnsureSuccessful() { Person person = new Person(29, THRESHOLD_NET_SALARY - 1); Assert.assertFalse(new SuccessfulFilter().test(person)); }}Copy the code


The book

With these three tests, we already have an idea of how the current code works: if someone is under 30 and earns $68,300 a year, they are considered successful. We can create more tests to ensure that the feature is correct in marginal cases (such as without age or salary). And once a suite of automated tests is built, it can be used to ensure that changes to existing code don’t break existing functionality.


The book

Other Translations (1)

Using existing tests

We can also learn a lot from testing, given that there are enough tests in existing code. Just as we create tests, we can read tests to see how the code works at the functional level. In addition, we can learn what the original author understood the code to do. Even if the test is not written by the original author but by someone else (before us), it can still provide us with someone else’s understanding of the intent of the code.

Even though the current tests are helpful, we still need to be cautious. It’s hard to tell if tests are consistent with code changes. If consistent, we have a solid foundation to understand the code; If not, we must be careful not to be misled. For example, if the original salary threshold was $75,000 per year and then changed to what we know is $68,330, this outdated test could lead us astray:

@ Test public void under30AndNettingThresholdEnsureSuccessful () {Person Person = new Person (29, 75000.0); Assert.assertTrue(new SuccessfulFilter().test(person)); }Copy the code


The book

The test will still pass, but not to the desired effect. It passed not because it was the right threshold, but because it was over the threshold. If the test set includes a test case that returns false if it is paid only $1 less than the threshold, the second test will fail, indicating that the threshold is wrong. If the suite does not have such tests, the old data can easily mislead us into the actual intent of the code. When in doubt, trust the code: As our front end shows, solving the threshold problem shows that the test is not targeting the actual threshold.

Also, refer to code base logs (such as Git logs) for code and test cases: If the code was last updated much more recently than the test was last updated (and there is significant code in the code, such as changing thresholds), then the tests may be outdated and need to be treated with caution. Be careful not to ignore them entirely, as they may also give us some information about the original author (or the developer who wrote the test recently), but they may contain outdated or incorrect data.


The book

2. Talk to the person who wrote the code

In any job that involves more than one person, communication is crucial. Whether on a corporate, cross-country trip or project, a lack of communication can have serious consequences. Even though we communicate when we create new code, the risk increases when we touch existing code. Because our knowledge of pre-existing code is limited and what we know can be misleading or one-sided, we need to talk to the people who wrote it in order to really understand existing code.


The book

When we ask questions, we want to make sure they are targeted to our purpose of understanding the code. Such as:

  • What part of the system blueprint does this code correspond to?

  • Do you have any design plans or diagrams?

  • Are there any potholes I should be aware of?

  • What does a component or class do?

  • Is there something you wanted to write in the code, but didn’t? Why is that?


The book

Other Translations (1)