← All posts

Why do we spend time teaching this?

What are the benefits of learning software engineering, and why do we consider it one of the most important topics in a career that mostly deals with work outside of the industry?

Every semester I start this course the same way: I ask the room why they’re here.

Not as a formality — I genuinely want to know. Most students are in their third or fourth year of a Computer Science degree at FaMAF, and by that point they’ve survived mathematics, algebra, program derivation, algorithms, and a semester’s worth of formal logic. FaMAF has a reputation for being one of the most rigorous CS programs in Argentina, and that’s well-earned. The students who make it to this point are capable, but more than that, they are resilient. They are, in every meaningful sense, survivors.

And most of them have no idea what a software developer does on a day to day basis.

They have no idea what the work of software development looks like in real practice, and they don’t know how to get from where they are now to being able to do that work.

That’s not a criticism. It’s just the reality of an academic program built deliberately around theoretical computer science and research (and it’s very good at what it does).

But it means that when students graduate and walk into their first job (and roughly 85% go into industry rather than academia) many practical elements of software development are foreign.

The Software Engineering course exists to close some of that gap, or at least provide the tools to do it.

Why the engineering process matters for a software developer

There’s a version of software development where you just write code.

You get a ticket to complete, you implement whatever is in there, you close the ticket. Repeat until Friday, mash those keys, clear up the slate. A lot of developers spend their entire careers in that mode. You can earn a paycheck doing it — but I don’t believe you can build a decent career as a software engineer that way. That’s just button-pushing. It’s digital assembly-line labor. 1920 style, nice.

But that’s not engineering. That doesn’t scale, not it creates software products. If every developer on a team just wrote code and nothing else, without any shared process or standards, without any shared understanding of how to make decisions, how to create a product, and how to communicate with each other and with clients — that would be chaos… and short-lived chaos at that. It would be a mess.

Engineering is about understanding the system you’re working in — I’m not talking about the software system here, but the framework in which an organization creates software. The code, process, team, constraints, and tradeoffs. These moving parts form an interconnected system, one that must adapt to new requirements, changing teammates, and shifting tech stacks, and you have to understand how all of those interact to be able to navigate it effectively.

It’s about building things that work and making sure they continue to work as they grow, change hands, and outlive their original authors. It’s understanding form and function, and how they interact. It’s about making intentional choices that balance short-term needs with long-term sustainability. Software engineering means taking a problem, designing a solution, and guiding a team to deliver it — all without breaking the codebase or driving the team crazy.

Which by the way, is no simple feat. It’s a skill that takes years to develop, and it’s one of the most valuable skills you can have as a software developer. A system that is built upon friction, that wears down the people who work on it, grinds down even the most talented developers while eroding the value of the software itself.

The engineering process matters because code doesn’t live in a vacuum. It lives inside the accumulated decisions of everyone who ever touched it, in a codebase that outlives its authors, inside a company that often doesn’t fully understand what it has built. Every choice ripples. Every shortcut compounds. Every good decision pays quiet dividends long after the person who made it has moved on.

Framing your work correctly

One of the most liberating shifts you can make early in your career is understanding that your job isn’t to write code — it’s to solve problems. Engineers are problem-solvers first. Software engineers solve problems by building software, but the software is just a means to an end. Code is just the primary tool you use to do that.

Code is the hammer, but the problem is rarely “drive this nail here.” It’s more like: the product is a screw, and it needs to be fixed on the second floor of a house with no stairs — while the house is actively on fire.

And you know what? the client isn’t entirely sure they want a screw in the first place — they mentioned nails in the original meeting.

My awesome analogy aside, the point is that code is not enough to solve the problem.

This reframe changes how you ask questions, how you push back on requirements, how you handle technical debt, and how you talk to non-technical stakeholders. By understanding that the software we build is always targeted at solving a problem, you can approach the way you write code with a broader and practical perspective.

You start thinking not only on form, but also in function, and how both interact.

This is an important step to start thinking about the way the whole engineering organization works, and how coders fit into it. Every line of code must be justified by the problem it’s solving, and every problem must be justified by the actual benefit it brings to the user.

Business Analysts and Product Managers, as the first contact points between the technical team and the users or clients, are responsible for defining those problems.

Developers are responsible for solving them in a way that is sustainable, maintainable, and scalable.

Quality engineers make sure the solutions we build actually solve the problems they were meant to solve, and that they continue to do so as they evolve.

You should be able to ask “why” and “what value does this bring” at any point in the process, and you should be able to answer those questions for your own work. Most of the problems junior developers face, are directly related to either:

  • A lack of understanding of the problem they’re trying to solve,
  • The inability to communicate effectively with the people who do understand the user’s needs.

They get lost in the code, and forget why they’re writing it in the first place.

Framing your work correctly turns you from I’m a developer that writes code into I’m a person who builds systems that solve problems. That’s a very different identity, and it comes with very different responsibilities. It also helps you see where you fit into the broader organization building the product.

Code goes way above technical correctness

Another problem that we normally see on the course is that students code away any task without considering the function the code serves. A function can be technically correct and still be a major disaster!

It can be too slow, too expensive, impossible to test, impossible to understand, or impossible to change — select all that apply.

Correctness is clearly necessary but not sufficient, and this is the first time most students are exposed to that idea.

In every other lab class before Software Engineering, they have been trained to write code that is correct to solve a specification, and that’s what they do. Apart from maybe indentation and Big O Notation efficiency, they usually don’t think about the other dimensions of code quality, because they haven’t been taught to (or, let’s be honest, they haven’t been taught to care about them).

They haven’t been really exposed to the idea that their code will be read and maintained by other people, that it needs to be easily understood, and that it will have to evolve over time.

And the fact that all of those things are just as important as it being correct in the first place.

If you want to be useful to the engineering organization as a whole, what you’re actually aiming for is code that is correct, maintainable, and readable — both to your future self and to your teammates.

That requires a level of craft and intentionality that doesn’t come from just learning syntax.

One of the traits that we try to cultivate in the course is the quality of the code. It is a mindset of craftsmanship, where you care about the quality of your work and take pride in it.

Another one is having the same care to the quality around the code itself: the tests, the documentation, the commit messages, the pull request description, and so on.

Finally, we also try to make students understand the idea that equally important is the quality of the soft skills of communication and collaboration. Making sure that the solution you work for is not only technically correct, but also well-communicated, and that it fits into the larger system of the organization.

The scale of the real world

In university courses you mostly write code alone, for assignments that will be run once by a grader, and then probably discarded.

The real world is different in almost every dimension: larger teams, longer timelines, codebases that have piled up changes for years, backlogs of technical debts for the ages, bugs that will give you nightmares.

Systems that have to run 24/7, and users (and business) depend on them.

Scale changes everything.

A naming convention that works fine across 10 files becomes a maintenance nightmare across 500. A shared variable that’s harmless in a prototype becomes a race condition that takes three engineers a week to track down in production.

Practices that work just fine for a 300-line assignment don’t survive contact with a 300k-line codebase maintained by 15 people across three time zones.

The isolation of the university environment, where you can focus on a single problem at a time, with all the information that you need right in front of you, and where every other component of the system works correctly — and plays nice — is nowhere close to the reality of software development in the industry. The real world is noisy, messy, and riddled with unknowns. The code is dark and full of terrors.

It requires a different set of skills, and a different set of tools.

The skills you need to navigate it are not in any textbook. Correction: maybe they are in some textbooks, but they cannot be learned by reading alone. They are earned by practice — slowly, and mostly in the wreckage of things that didn’t go as planned.

Conclusions

I greatly enjoy the Software Engineering course.

It was one of my favorite courses as a student. It’s my favorite course to teach, and I’ve been teaching it for over a decade now. I get to share with students the things I’ve learned from my experience in the industry, and I get to see them grow and develop as engineers, which is probably the most rewarding thing about teaching.

But we rarely work alongside our past students, so it’s hard to know for sure how much of what we teach actually sticks with them and helps them in their careers.

On the other hand, we know exactly what happens when people don’t learn these topics — we all have worked with that kind of software developer. Deep down — or not so deep in some cases — we all come to regret it.

I believe the course works. A decade of watching students go through it, stumble, correct, and come out the other side knowing how to think about their work has been enough evidence.

This series is my attempt to put into writing what we actually teach — not the theory, not the syntax, but the things that I believe determine whether you will be effective outside the uni environment, on a real software team.