The journey from theory to practice in software engineering - Part 2

Part 1 can be found here.

In Part 1, I shared my experiences with the gap between academic study and industry-required practical skills. I emphasised the importance of understanding core computer science theories, such as algorithms and computational complexity, and how these theories underpin critical areas of software engineering, including cybersecurity. I also discussed the challenges faced by new graduates, like debugging and managing real-world data, setting the stage for Part 2, where I focus on bridging these gaps through practical application.

This time, we’ll explore where theory and practice work together, why we sometimes need to reach back to academic knowledge and where the right balance is. Let’s start with a specific application of theoretical computer science.

Theory applications

You may ask yourself where you are going to use this abstract knowledge, especially when you start your first job and see how different it is from your previous curriculum. Let’s examine a few areas where immediate applications of the theory are more apparent.

Job interviews

The journey from theory to practice in software engineering - part 2

Whether you like it or not, the reality is that a lot of companies use algorithmic questions in their interviews. These are usually not knowledge-based questions, but you must know the basics. What’s important is that you need to learn how to approach these questions and learn some patterns to solve them. You’ll rarely encounter the same task you practised in an interview, so it’s more about developing an intuition on how to approach it.

One of my past interviews involved designing an in-memory SQL database. All operations, which included: “SET x a”, “GET x”, and “COUNT a”, were required to be O(log(n)). The system was also required to support single-threaded transactions. I’ve never tried to implement it before, so it took me a moment to come up with proper data structures that would support my solution. I had to use one Dictionary for a key-value store, another for efficiently querying the count of values and a 2D dynamic array to support the transactions. This problem didn’t require me to know any particular algorithm but a good familiarity with data structures’ complexity and trade-offs so that I could provide the answer within the given time constraints.

Certain companies will also allow you less work experience if you have a PhD or Masters degree. So, if you’re enthusiastic about staying at the university, it doesn’t have to mean you will have worse job opportunities later.

Let’s see how we can apply our knowledge after getting the job.

Day-to-day ideas

The journey from theory to practice in software engineering - part 2

A lot of programmers' daily job involves googling. Googling documentation, googling problems, googling syntax. But certain problems are only yours to solve. They are either too domain-specific or too complex to have a simple guide on the Internet. Or you just have your idea of solving them and don’t feel like googling. If your particular issue could use a graph algorithm or a prefix tree, then if you can’t recognise it, you won’t use it. Even if the solution is perfect for the job. Certain things are good to have in the back of your head so you can use them when you notice a schema.

Consider a more generic example of the MapReduce model (if you want to look up the original paper click here).

The journey from theory to practice in software engineering - part 2

It’s a very simple idea – breaking down big data problems into smaller blocks to process in parallel and then aggregating the results. If this model can be applied to your problem, it could significantly improve the scalability and performance of your solution. You have to be aware of this idea and notice it at the right time to apply it. No StackOverflow post will give you comprehensive advice on how to design your system. It’s going to be your job, and the more solutions you have seen, the better you will be at choosing the right one.

Let’s examine now what you can do if you are an advanced computer scientist.

Innovation and research

The journey from theory to practice in software engineering - part 2

Engaging in innovation and research allows you to apply theoretical knowledge to explore new technologies or methodologies. This could involve working on cutting-edge projects in areas like artificial intelligence, quantum computing, or blockchain. Theoretical foundations enable you to contribute to the academic community through research papers or patents, potentially leading to breakthroughs that shape the future of technology. Even if you don’t work directly in computer science research, this knowledge can be beneficial in certain occupations like data science, cryptography, or compiler design.

Let’s take data science as an example. To work in this occupation, it’s very useful to have a strong background in:

  • Statistics: Understanding probability distributions, statistical tests, and inference methods to analyse and interpret data.
  • Machine learning algorithms: Knowledge of supervised and unsupervised learning models, including decision trees, neural networks, and clustering techniques for predictive modelling and data analysis.
  • Linear algebra: Essential for machine learning and deep learning models, including understanding vectors, matrices, and their operations.
  • Optimisation techniques: Algorithms for finding the maximum or minimum of functions critical in machine learning model training.

This theoretical knowledge is not easy to acquire on an ad hoc basis, with trial and error learning. It requires careful reading and study and will be very useful in your future job.

In the next section, we’ll examine how to balance our theoretical and practical skills effectively.

The balance

The journey from theory to practice in software engineering - part 2

(source: Reddit)

As you've likely concluded, both theory and practice are integral to the software engineer occupation. You’ll be in a less advantaged position if you neglect one or the other.

There are two direct options for imbalance: not enough theory or practice. Let’s see how we may find ourselves in these situations and how to address them.

Not enough theory

It’s hard to say that someone may have “too much practice”, but the time spent only on the practical parts may interfere with learning the fundamentals. How may we find ourselves in this situation? It may be a case of ignoring what you would otherwise learn at university. You pass the courses and forget whatever was required afterwards, considering it’s mostly irrelevant to your prospects. Or it may be that you skipped the university because you wanted to finally earn your own money. Alternatively, you may have been in a cosy programming job that didn’t strictly require you to use much of DSA (Data Structures and Algorithms) for the past X years. You just forgot most of it. Regardless of the reason, you no longer recall how to write a BFS, implement a merge sort or even do a binary search.

It may seem that you don’t need to remember any of these things.

And maybe you don’t. Not for your usual responsibilities. I am not trying to persuade you here that DSA is the core of your daily work. We all know the reality of a large portion of programming jobs. What I’m trying to convince you of is that this knowledge is valuable. Useful for you and your career. It will be a thing that distinguishes you from others.

At some point in your career, when you want to grow, you will reach a limit to what you can acquire from language/framework expertise. Strong theoretical knowledge will help you solve problems that others can’t. It will help you to find a better job. It will make your solutions more advanced and smart. It will make you stand out from others.

How to learn DSA?

I’d recommend starting with a book or an online course, which will solidify your understanding. A nice, easy-to-read book on the subject is “Grokking Algorithms: An Illustrated Guide for Programmers and Other Curious People”.

Only after you have a reasonable comprehension of the DSA may you start practising your skills. And the easiest way to do this is to solve problems on platforms like LeetCode, HackerRank, or CodeChef. My advice would be to pick up some problems there and then try to get an idea on paper first. It’s painful and slow, and that's the point. It makes you reflect rather than play back and forth with the code. It makes you reason about your test cases rather than submitting your solution against existing ones (similar to real-life experience). Learning on paper makes you intentionally write less and think more.

After you resolve a problem on paper, you may try to code it and see how many mistakes you’ve made. Repeat the process for tens of problems from different DSA topics, and you’ll notice satisfying progress in your understanding.

Not enough practice

I think it’s less common in software engineering, but it still can happen, especially at the beginning of your career. As one example, you may have spent a lot of time learning various algorithms or practising problems on HackerRank or LeetCode. You’re able to pass a big-tech company interview. However, when it comes to working with real-world systems, you are unable to apply your knowledge efficiently. Or you may be switching from a long time in academia and research to a more hands-on position in tech. Your problem-solving skills may be outstanding, but they are mostly useless if you can’t employ them in a concrete system. You may have amazing algorithmic abilities, but software engineering is not only about computer science.

How to practice software engineering?

The easiest way is to get a tech job. Nothing will give you a better real-world experience than a real-world experience. When it’s your first time investigating a bug in a 200k lines project or expanding a 10-year codebase, it forces you to think on a much different level in comparison to your small project where you can remember and comprehend every part of the code. If you want to practise on your own, try something else than small-scale algorithmic problems, for example, a personal programming project. Some ideas I can recommend (a few more examples can be found here):

  • Chess game
  • Distributed agents game, where two sets of autonomous agents play according to a common set of rules to accomplish a specified goal
  • Web scraper
  • Your social networking website

If you have this possibility, do this project with a few other people. It’s very different when you have to collaborate with others. You may even try to work on an open-source project. As you grow in experience, you may also explore programming theory different from DSA, the theory of software engineering. It’s more valuable to study once you have some experience under your belt and directly witness the pain points of real-world systems. Some of the classics include (a more comprehensive list can be found here):

  • “Clean Code”
  • “The Pragmatic Programmer”
  • “Designing Data-Intensive Applications”
  • “Code Complete”

Common challenges for beginners

Let’s examine a few challenges for novice software engineers that stem from a lack of practice:

1. Overwhelming number of languages/frameworks/techniques

Imagine you’re working on a Web application with C#, JS and SQL stack. If you know C#, JS and SQL, you should be fine, right? No. Not at all. If you are working with this stack, your project may use (for example) ASP.NET Core, React, Bootstrap, Entity Framework Core along with countless other libraries that you didn’t work with before. Knowledge of the language isn’t knowledge of the environment and tools around it. Needless to say, you will also have to learn how your specific project is organised and what conventions you use. What may help here? Try to develop a personal project from scratch to understand why specific libraries are used and how they are helpful. Writing something from scratch will give you a much more thorough understanding than just editing bits and pieces of existing code.

2. Over-optimisation

If you were deep in the DSA realm before, you may be used to creating tiny optimisations to gain a few more milliseconds in the test case execution. Except for very specific cases, spending time on them in a real system is superfluous. O(1) is not always better than O(n) (a nice example can be found here). Bit operations are rarely really utilised because of poorer readability. The impact of a small optimisation will be lost on the main bottlenecks of the app, such as I/O, database access, or external network calls. Focus on the main bottlenecks that have the biggest impact on the response time.

3. Over-refactoring

This is a very personal one. I was very fascinated by the idea of clean code even before I started my first job. I used to make a lot of PR comments while collaborating on study projects because the code HAD to be clean. When I started my first job, I saw sooo many issues in the existing code that I could easily fix – naming inconsistencies, formatting issues, unused imports, etc. As a result, my initial PRs usually included a few thousand changed lines due to on-the-fly refactoring. Don’t do it. There’s a delicate balance here, but it’s usually better to make a big-scale refactoring its PR and ticket. Unless it can be automated and enforced, you will only fix the issue temporarily. Clean PRs usually have a very strict focus and don’t allow other unnecessary changes to creep in.

Summary

This post explored the practical applications of the theoretical knowledge discussed in Part 1. Through examples like job interview preparations, day-to-day problem-solving, and innovation in cutting-edge technology fields, we illustrated the application of computer science principles. I shared insights on balancing theoretical understanding with practical skills, emphasising the importance of learning and adaptation in the fast-evolving field of software engineering. I illustrated how engaging with real-world projects, participating in coding challenges, and devising your own applications can significantly enhance your practical skills and efficiency. This part aims to arm you with insights for leveraging theoretical depth alongside practical skills for a successful software engineering career.

I hope that I persuaded you, at least a bit, that what we learn at the university isn’t irrelevant to our daily jobs, but it isn’t a complete education either. Our occupation has engraved “continuous learning” in the job description, and that’s probably why it’s so satisfying :)

Connect with me on LinkedIn or email me to share your experiences.

Share the happiness :)