Ten lessons I’ve learned becoming a software engineer
I recently celebrated the three-year anniversary of landing my first job as a software engineer. Being completely self-taught (I studied classics, would you believe it!), I think it is important to recognize such occasions as an opportunity to reflect on the way things are going and the lessons I’ve learned along the way. And since my experience might be relevant for anyone who is carving out their path through the world of software, I want to share the ten most valuable lessons I’ve learned becoming a software engineer.
Think of it as the advice I wish someone gave me when I first started…
The fundamentals
When I first moved to my current firm, I knew some SQL and I dabbled a bit in Python, but I was sorely lacking a basic understanding of the fundamentals of computer science. I’m not ashamed to admit that one day during a training session I excused myself to go to the bathroom to lookup the difference between “bits” and “bytes” on my phone…
So the first lesson I’ve learned is without doubt that although you don’t need a degree in Computer Science, some fundamental knowledge is indispensable. Especially two areas come to mind: first, a basic understanding of computer science, such as memory management, time complexity, character encoding, and so on, and second, a fair grasp op software design patterns, such as MVC or different types of concurrency and parallelism.
To make matters concrete: if you don’t really know when to use a Python dictionary instead of list or you are not sure what variable scope means, you should hit the books…
Programming languages don’t matter
When I teach my university course of Information Science, my students — who are humanities masters who have just left Python boot camp — often ask about other programming languages. When you’re first getting started in a certain language, other tools or paradigms sure have an aura of mystery and appeal. As a novice, I too was impressed when I heard someone was fluent in several programming languages.
Along the way, though, I have come to the conclusion that programming languages don’t matter. Even more so, once you truly find out what programming is about, you realize that they’re all the same. As Rich Hickey supposedly once said: Programming is not about typing, it’s about thinking. Languages and frameworks are just tools for a job. They possess no inherent value. That’s why we use the relatively unknown MUMPS at my firm, although you can find critical articles aplenty about. For us, it’s the right tool for the job. So don’t get caught up in debates about this or that Javascript framework du jour. A fool with a tool is still a fool…
Good code is underwhelming
Something else everyone on the Internet seems to have an opinion about, is the qualities of “good” code. Some will stress clarity, others advocate clean code, some focus on reusability or self-documenting code…
Although such reflections are definitely interesting, my impression is that it is really difficult to come up with truly general guidelines for good code. Sometimes code needs to be fast, sometimes it needs to be explicit, sometimes it needs to be quick and dirty… Software also goes through various phases.
But one thing that I have noticed is that good code is often underwhelming. I learned this from working on a Go application for software deployment developed by my CTO, where every function seems so plain, so short and so unimpressive that you get the feeling there is really not much software to look at. But every piece of code does exactly what it needs to do and when all the pieces come together they form an intricate whole… And that’s pretty neat.
The importance of metadata
One often-heard advice about code is that it needs to be generic. This leads to another very important lesson that I’ve learned about software. When you first start out, it is often tempting to solve everything in code. Over time, however, I’ve learned to combine software with data as well.
What I mean is this: suppose a friend asks you to write a script that generates invoices. In could look something like this (obviously, this is almost pseudo-code and not a real invoicing application!)
company_name = "A"
company_account = "123-456-789"
company_address = "Abbey Road 1, London"
company_phone = "+32-000-456-789"client = input("Client?")
amount = input("Amount?")
date = input("Date?")
invoice_number = input("Invoice number?")invoice = f'''Invoice: {invoice_number}
Date: {date}Dear {client},please pay the amount of {amount} Euros
to bank account {company_account}
in name of {company_name}, located at {company_address}.For more information, contact {company_phone}'''print(invoice)A script like this will definitely work, but it will only work for your friend’s company. However, instead of just hard-coding a bunch of company_* variables in the script, you could also store these in a JSON or YAML file and then read that file in the script. You could even store the invoicetemplate as metadata. And just like that, you’ve gone from a script for a friend to generic invoice software that can be used by thousands of companies, each of which can define their own parameters and templates…
Do not shy away from difficult problems
Designing software in a smart and efficient way, writing good code, understanding different languages and the internal workings of the wonderful “computing machine”, … — it’s all far from easy. Some of it comes pretty quickly, but along the way there will be challenges and frustrations. If the vast community of programmers shares one thing, it’s probably a love of solving problems. And computing problems can be fiendishly difficult.
On my journey I’ve experienced many times that no matter how long you put off digging deep into a particularly difficult problem, you will need to face it eventually. Character encoding, for instance, is a traditional example of a topic many professional programmers avoid for years, until a project finally forces them to tackle it. Personally, I think I’ve sometimes shied away from such problems out of fear to produce imperfect code, but now I realize that there’s no better way to handle the difficult stuff than head on…
You will never finish a project
Another lesson that ties in with the previous one is the realization that you will never finish a project. What I mean is that if you write software professionally, your code will often go into production before you think it’s 100% finished. I’m not advocating shipping inferior software here, I’m talking about the fact that software is a living, breathing thing that, in interaction with its users and changing technologies, must be allowed to evolve. It’s inevitable that there will be bugs, that you will refactor code, that users will request features you wish you had thought of earlier, that over time modules will need a rewrite, maybe even in another programming language. Someone should write a book about the life of something like Microsoft Word. I think we would be astonished to learn how much a product like this changes over time…
Fail fast and fail loud
Testing is an issue I have struggled with in the past, and to be honest, with which I am still struggling. Writing unit tests is one thing, but when it comes to integration and system testing, especially when developing a complicated suite of software that relies on dynamic user data, I still find it hard to come up with good tests and to balance my time between writing test code and application code.
However, as far as the principle is concerned, this is another thing I’ve had to learn the hard way: good software needs to fail fast and fail loud. After all, if there is a problem with your software, you need to know as quickly and as clearly as possible. For instance, once I was developing a core API function that read some piece of user information from our database. I wrote it so that the function returned an empty string when the API called on a non-existing user. However, as one of our seniors pointed out, it is much better to return or raise an error at this point in the software. Mind you, the API software is different from the web interface here, which should keep invalid user IDs from being passed to the API. But, at the low level of the API, obscuring the fact that a piece of data does not exist, is definitely an anti-pattern.
Put away your pride
It’s at moments like this that I’ve also learned to put away my pride. Of course if you invest a lot of time and energy into your software, you are going to be frustrated when things get difficult, when your software comes crashing down or when your colleagues are critical of your product. However, you need to put away your pride and learn from your mistakes, without subsequently wallowing in imposter syndrome. Own your mistakes and own your successes.
Stress management
The stereotypical image of a software engineer is probably of a person who sits quietly all day coding away peacefully — at least mine was. Over time, I came to realize that software development is much more about stress management than you would think. In our line of work, when things go wrong, they often go wrong spectacularly, and there’s usually someone to blame.
I remember one time when our systems engineer experimented with the rsynccommand to backup the code repository on our development server, which resulted in wiping almost the whole server clean! But while the situation was quite hairy, I also remember he remained pretty calm and restored the server from backup almost jokingly. At an earlier point in my career, I desperately envied such peace of mind and have since worked a lot on my personal coping skills for stress.
Life-long learning
Finally, this list would not be complete without the observation that in the future there will be many more lessons to learn. And that’s also what I love most about software: the opportunity to be amazed on a daily basis by the possibilities of technology and the resourcefulness of the human mind. For me, it’s one of the great pleasures of life…
Hi! 👋 I’m Tom. I’m a software engineer, a technical writer and IT burnout coach. If you want to get in touch, check out https://tomdeneire.github.io






