Things I have learned since leaving university

3 Mar 2019

In a few days (March 6, 2019), I will celebrate my two years as a full-time employee with my current employer. This is my first job out of university, before that my only noticeable employment has been a 7 months internship for the same employer. This got me thinking about how my perspective on software engineering has changed and the practical skills I have learnt.

I wrote the following sections in the order I encountered the concepts, they are therefore not in any importance order.

Less is more, deleting is good

One of my first tasks as an intern was to change some logic from using one database field to using an other that was populated differently. This old field had to be entirely decommissioned and it was used all over the codebase. The investigation led me to finding a large series of classes that were not called from anywhere but happened to reference themselves a lot. This was all dead code that used the field I was supposed to remove any trace of, I approached my manager and asked him what was the best course of action. He simply told me to get rid of all of it and he had a big smile on his face.
In university project, I had often found myself really proud of the number of lines some of my projects were, I was proud of how big of a project I had managed. What I did not get though, is that after more than 10 years of development on a huge project, any line you remove becomes a win because it is a line you don't have to maintain anymore. That is why my manager was so happy about it, I had just discovered a big chunk of code the team did not have to care about anymore!

Your code will die, sometimes earlier than you think

The question of code longevity is a very tricky one. In a world that changes so dramatically quickly as ours, the requirements evolve very fast, especially when following agile development techniques. It is very hard to predict how long a piece of code you wrote will live and be useful.

The first time I encountered some of my code living for shorter than I expected was during my internship. I had designed a reporting mechanism to send some data report to a group of business analysts on a schedule. I was told there would probably be multiple of those and I made sure my implementation was reusable and could handle multiple types of inputs and outputs. About two months later the entire reporting system was turned off because the reports I had implemented were not needed anymore. I thought they might be revived. Six months later, when I came back as a full-time employee I finally deleted the code, it had not been used at all since I turned off the initial reports.

All of this is fine, this was not my fault (the code was behaving as expected), this is a simple case of evolving requirements. Deleting the code was the right thing to do (you don't want to lose time in making sure it still works when it is clearly not needed anymore). In fact, it would have been damaging for me to cling to this piece of software I was quite proud of. The lesson here is that it is okay to retire code you wrote, as mentioned earlier deleting is good and makes everybody happier!

Make it as simple as possible, but not simpler

As I mentioned in my previous section, some of your code will die rapidly and unexpectedly. But you can also see some 30 years old code that is still working and generating revenue for the company. As I said, predicting the longevity of code is very hard. The longevity of concepts is also an interesting question: while some particular piece of code might not last for long, it might inspire other work and the concepts it introduces might outlive it by years. There is an aphorism often attributed to Albert Einstein (it might not be him) that says "Everything should be as simple as possible, but not simpler". I think it summarises well how I think about this problem right now: you should always go for the simplest possible solution with the smallest possible amount of abstractions. It is important to note that "as simple as possible" does not mean that you have to copy paste all over the place and not abstract anything in your code. It means that you should not create abstractions when they are not needed. This is one of the main contrasts with what I learned in university. We were told to always think of all the ways we can add abstract classes, interfaces and factoring out everything in small functions from the beginning. When going out of university, I thought that this was always the best way, hide your logic behind layers of abstractions.
The reality of it is that if you have only one implementation, then creating an abstract parent class or a "common interface" for potential future similar implementations pretty much guarantees that you will get the abstraction wrong. I often realise that before copying and pasting the code at least once I don't really realise which parts of it are truly reusable and common. And if you think about the story I told in the previous section, if your first implementation is a fully fledged framework, you have a high risk of generating a huge amount of complicated code that you will end up throwing away.
That being said, it might be good to mention that in some cases, with some languages and technologies, you might have to introduce some abstractions early on for practical purposes such as mocking and testing, this is okay, it is always better to have well tested code!

Legacy is everywhere

You will encounter legacy code. This is pretty much guaranteed, it can be very old (more than 10 years old) or more recent (a few months old) but you will encounter it. What makes it legacy is often that it has been written with a different set of requirements and standards than you have now. This is normal in the life cycle of a software project, I had the honour of updating some of my own legacy code in the last two years. What is important here is to realise that this code was written in a particular context and that the people who wrote it were probably doing their best given the constraints they had. Once you recognize this and understand what the code was supposed to solve, then you can probably make the appropriate decision. Sometimes this decision will be to remove the code entirely and replace it with something shinier, but don't always disregard what was done in the past, you will often find that if it was developed in this peculiar way it was for good reasons and when trying to do it differently you will have to overcome a whole new set of challenges!

Building a product is not just writing code

I think this is a lesson I have constantly learned over the past two years. Writing code and solving the technical challenges is only a (sometimes small) part of building a product. Sometimes the product gets derailed and/or delayed by some other factors. Many of these are often completely hidden from university students, I will try to summarise the main ones in this section.

Defining the problem

It might seem obvious when I say it, but if you want to solve a problem, you have to define the problem. I have seen quite a few instances where the problem was not very well defined (there was only a very high level definition) and what usually ends up happening is that you either build the wrong product or nothing gets built at all because you lose yourself in endless discussions.
Sometimes it falls to the software engineer to realise that the problem is ill-defined and that they have to go back to the stakeholders and ask for more precise information.

Getting to work on the right problem

When you work in a team that already supports many systems. You will have to share your time between building new products and maintaining old ones. Some even newer products might even come to you and force you to put aside what you were working on to focus on the newest, potentially more important thing. In many cases you will have some external stakeholder who is responsible for deciding what is the priority for the team and you don't have to think too much about it.
However, it is still your responsibility as an engineer to prevent problems: if you think that the set priority is wrong because you think that something else is more urgent, it is your duty to collect evidence and show why to the stakeholders. They might not know everything and it is useful for them to hear your constructive feedback.

Depending on others

If you join a big company, or any company that has more than one team of engineers, you will often depend on the work of other people in other teams. These people have their own priorities that don't necessarily align with yours. It is very important to be able to communicate how urgent your need is and collaborate with the engineer you depend on to make sure everybody's priorities are taken into account. This sometimes mean just sending a few texts, it might mean long email chains and meetings. It is important to realise that none of this is personal and that all the parties involved are working for the same company. Everybody is trying to do their best for the company and it is important not to lose sight of the fact that everybody is playing for the same team.

Shipping and maintaining is hard

Beyond simply solving the core technical aspect, you will need to make sure you can maintain your code efficiently. I am not only talking of testing. I am talking about how you ship/deploy your code. Do you have a back out plan of your current release is buggy? What systems are in place in order to monitor the health of your production environment? Do you have a proper separate production environment? I find that a lot of time has to be allocated to solving those questions efficiently. If the company is big enough, you will probably find that some team is responsible for providing tools that help you do that efficiently, but using these tools in the most optimal way is still your responsibility.
Maintaining software is a day-to-day struggle, making smart choices early on in the development process is key to make sure your life is as easy as it can be, but not easier.

Some last words

I probably forget a lot of things, writing code professionally is very different from writing code at home, at school or in a hackathon. Most of the differences come from being in a world that is constantly evolving and having to maintain the software over many years, sometimes decades. This is a big part in what makes my job interesting. It is also the main reason my work is valuable, if it was never confronted with reality it would probably mean that it was not doing anything useful to anybody.
I am at the start of my career, it is very likely that I will learn much more and that my understanding of the world of software engineering will evolve drastically. This is an exciting adventure and I hope to write a similar post in a few years with new interesting learnings!