Beating the Tutorial

Most software engineer job descriptions will have a requirement like this :

Has the ability to deliver ticketed tasks promptly and to a high quality standard.

This is well and good, it’s the primary gameplay loop of software engineering afterall. Receive ticket, make changes to match the behavior described in the ticket, make sure your code is reasonably readable and documented, deploy code to main. Via this mechanism, you deliver value.

After a time, you gain confidence in this, your peers and managers will praise you for your ability to do these tasks mostly unaided. The product becomes malleable to you, you start to think there isn’t any task you can’t accomplish given enough time. Heck, maybe you could even rewrite the entire product.

Congratulations on beating the tutorial.

Most organizations would have already promoted you to senior engineer by this point. This is an industry-wide mistake. Whilst I won’t go so far to encourage folk to turn down promotions, I will encourage them to avoid conceptualizing themselves as experts before they are ready. The real journey has only just begun.

Being able to deliver any given feature somehow is table stakes. Up until this point, you have not been contributing very much to your organization, not really, in fact you’ve probably spent a significant part of your career being a net-negative contributor in terms of absolute product value. This may seem shocking, clearly you’ve been delivering features, probably some customers even find them useful, but this is missing the point.

All change has cost, and although the organization will assert that the value of ticket delivery is always worth the cost of change, (otherwise they wouldn’t have asked for the feature right?) the truth is more complicated.

Creating any one single behavior in a computer system is almost always trivial for the experienced engineer. When the experienced engineer on your team says that something can’t be done easily, they almost always mean is that the thing can’t be done easily in a way that is acceptable to the health of the product. Junior engineers tend not to have to consider this constraint. Especially on teams that are more feature-mills, junior engineers will frequently add features in ways that are at best value-neutral, and at worst value-negative over the lifetime of the product.

This is intentional! All things exist in contrast, good and bad are paired, you must be allowed to fail in ways both varied and numerous in order to figure out what success even looks like. The technical growth that comes from this sort of work is the point.

It therefore worries me when I see newer engineers talking about their careers as though feature delivery is the final goal of technical growth.

This is a systemic failure to lead, but the pushing of this attitude is also arguably an intentional and malicious attempt to commoditize the craft of engineering to the benefit of a privileged few at the detriment of all software users. LLMs are a recent extreme accelerant to this trend, but they are not the cause, it’s been happening for a while.

For any given business need, I normally consider dozens of approaches to achieve the desired outcomes. Some match the expectations of the ticket author, some don’t, but nonetheless fulfill the actual requirements. Some approaches are high risk, some low. Some manifest their value in that they require no collaboration with other teams, some only work if there is an expert available for integration. All are immediately viable, all have trade-offs, many are secret dead-ends that will make your product less competitive in ways that are utterly illegible to the rest of the organization.

Beyond even that though, there are sometimes viable options that leave the systems we steward in a better place than they were before, and this is important. You want to get exponential? Here’s where it happens. What I mean when I say better here is undefinable, it’s a highly connotatively connected property that encapsulates business necessities, predicting the future, interpersonal relations, technical realities, ethics & culture, etc. These options don’t always exist, but they will tend to stop presenting themselves if an organization habitually avoids/is unable to identify them, and will present themselves more readily in the inverse case.

Exploring the shape of this make better quality across different contexts is the actual game of software engineering, and doing so will take you much, much longer than merely figuring out how to deliver features faster.

Perhaps this is also still just the tutorial. I’ll let you know if I beat it.

* I’d rather stop using the word feature, it belies a false perspective on what good technical work actually is and how it comes to be, but given these blogs posts are supposed to train me how to write without over-qualifying everything I say, I’ll just make do with this footnote.