After just over a year in the industry, I wanted to reflect on my experience and the things I learned that helped me on my continuous journey to becoming an effective software engineer. To achieve this I put together a number of guidelines I learned (mostly the hard way) that helped me to ensure I produce solid quality code and continue to develop my skills.
1. Code readability over over-optimisations 📖
One of the first things you learn when working in a team is the value of clean and readable code. Remember all those times you returned to a piece of code you wrote a few weeks earlier and had no idea what was going on? That is how another teammate feels every time they read a piece of your code that is not written in a readable and clean way. Sure it might have the best runtime possible, but what good is that when a bug is detected, needs patching and they simply cannot wrap their head around what you were thinking when you wrote the code? Even worse, they try to patch the ugly code by adding even more ugly code, supporting the broken window theory.
A good heuristic in this space is: if the code is complex, add a quick comment indicating the intention, someone (or even yourself) will thank you later and you avoid the risk of broken windows. Better yet, if you can write it such that no comment is needed, do it!
2. The importance of small incremental changes over a large self-contained Pull Requests 📈
I know the first thing you are going to say: "but what if I need to quickly revert the changes in production?". Well sure, it may be more difficult to revert multiple smaller PRs, but trust me, the advantages of shipping small incremental changes much outweigh the disadvantages. First of all, shipping smaller changes means that a lot of code already lives in production before the feature is released, even if behind a feature flag. This gives you more opportunity to uncover bugs and iron out snags by testing the live changes, and gives more confidence that things will not break when a feature is shipped in one large swoop. It also aids in preventing the anti-pattern of sitting on unshipped code, outlined in Kanban as inventory overflow.
Secondly, maintaining a long-lived feature branch becomes a nightmare when trying to manage and resolve merge conflicts. My only advice is: avoid this if you can. Make use of feature flags wherever possible to get changes out without affecting customers.
Finally, nobody likes reviewing a large PR. Not only does it require taking more time out of their day to successfully review it, but also means they may focus less on the individual changes and miss or overlook opportunities for improvement or even bugs! As well as this, you lose out on the opportunity to get solid constructive feedback on your work from your peers, which allows you to improve your ability to produce clean and good quality code.
3. The value of pair programming and verbalising the approach to solve problems 🦆
After the college experience, we were all scarred by those projects that involved group work. But the reality of Software Engineering is that you work as a team and you should use this fact to help you complete your tasks. If stuck on a problem, it can be hugely beneficial to engage in a pair programming session with a teammate. I find this works best when using IDE extensions such as VScode Live Share (along with a voice chat app if working remotely), and chatting about the problem whilst looking at the same lines of code. You'd be surprised, but in many of those sessions, I realised that simply explaining the problem using the rubber duck principle leads to an epiphany, where you suddenly find your solution.
As well as this, pair programming sessions often help to mitigate information silos, especially when pairing with a team member that has more expertise on the problem at hand.
Finally, pair programming sessions are excellent practise for coding interviews. If stuck in an interview just, like when pair programming, simply discuss the problem you are having. This can either lead you to a solution, or it may prompt the interviewer to give you pointers in the right direction if you are both on the same page.
4. Computer science-y problems in interviews rarely represent tasks expected of the job 🧩
The heading speaks for itself. Rarely if ever in your career, you will be asked to implement a known computer science algorithm from scratch (and if you do, there should be alarm bells ringing in your head!). In 99.9% of cases, you will simply grab a ready-to-use library algorithm to complete the task. While it definitely helps to understand what is going on under the hood, it's not absolutely required.
5. Inheritance is not the gospel, composition is the way to go 🧬
From day one of programming modules in college, you are told about the magic of "object-oriented programming" and how "inheritance" leads to bug-free code, but sadly in the industry, it's not all rainbows and ponies.
In theory, the idea of inheritance is idyllic but rarely works in practise and frequently causes more problems than it solves. The tight coupling associated with inheritance results in less flexible code and makes refactoring more difficult later down the line, as whenever a parent class requires some modifications all children are inevitably affected. When considering inheritance, ask yourself the question "Can I use composition instead?". Almost in every case, composition is the way to go and supports flexible patterns such as dependency injection.
6. Don't be scared of Types 🆎
As a beginner programmer, it is easy to become scared of types, especially when starting with a duck typed language such as Python. It can feel like an unnecessary obstacle, but secretly it saves you a lot more time in the long run (even if you feel like you waste precious minutes every time you define a method).
In large codebases, types become indispensable. They provide a sanity check for free and cannot compare to the effort and time required on constructing trivial tests ensuring method inputs satisfy expectations.
What I am trying to get across here is, if you can, use TypeScript instead of JavaScript.
7. Write up a quick plan and get feedback early before jumping straight into coding 📑
Though it might sound like the long-winded and dreaded waterfall approach of developing software, writing a quick plan is hugely beneficial and allows problems and bugs to be eliminated earlier without having to re-write code.
The emphasis here is on "quick". State the problem, list out possible solutions (bonus points for having more than one), indicate your favourite approach, and share with your team asking for feedback.
Additionally, before committing to the final solution, it can be insightful to make use of Tracer Bullets and Prototypes. This is of course, once you learn to detach yourself from the guilt of throwing away prototype code.
8. Not using Web App frameworks is like coding with your arm tied behind your back 🏗️
Coming out of college with a Computer Science degree, I never had a single module on web app frameworks. This always puzzled me because it seems like a vast majority of this industry depends on using frameworks in their product whether a backend MVC or a frontend user interface library. Lack of coverage on frameworks in university education is definitely disadvantageous and an unnecessary barrier to finding an industry job.
So if you have no exposure to frameworks in your coursework, make sure to do your own homework! If you're curious where to start, my current favourite frameworks are Ruby on Rails and React for UI (with TypeScript of course!).
9. Demystifying the coding interview 🧑🏫
Everyone hates the coding interview, here are a few things I have learned from sitting on the other side of the table (or the other side of the Zoom meeting in this case!):
The interviewer isn't there to catch you out, they want you to succeed but if you don't try to communicate and collaborate with them when solving the problem, they won't force you to speak up.
Make sure to treat it like a pair programming session and demonstrate how you approach and think about the problem and solution.
Ask questions! The more you ask the better! Don't try to proceed, masking the fact you don't understand a detail or two. Get clarification. This demonstrates effective communication and shows you are capable of working with others.
It's not about finding the most optimised solution in the shortest amount of time, it's about showing that you can write clean code, using appropriate conventions whilst effectively working in a team (the interviewer and yourself!). Most of the time it doesn't even matter if you get the problem finished, if you show yourself to be a solid programmer.
Check out another blog post written by my colleague on this topic: https://engineering.tines.com/blog/how-to-pass-our-interview
10. Read these books! 📚
Though these concepts and principles are often covered in a Software Engineering module, it's hard to identify with them until you see them in practise once joining the industry. To refresh your memory or learn a couple of new concepts, the following book list is a good starting point:
The Pragmatic Programmer by David Thomas
Mythical Man-Month, The: Essays on Software Engineering by Frederick Brooks Jr
Clean Code: A Handbook of Agile Software Craftsmanship by Robert Martin
Cracking the Coding Interview by Gayle Laakmann McDowell